diff options
| author | David T. Sadler <davidtsadler@googlemail.com> | 2021-05-19 23:46:22 +0100 |
|---|---|---|
| committer | David T. Sadler <davidtsadler@googlemail.com> | 2021-05-19 23:46:22 +0100 |
| commit | 62243cd0b5262e14df7e7b5b7103b5b2e006cab5 (patch) | |
| tree | 2efe608a92f42aca3c372712fc14d4ae10fcec72 | |
| parent | bbf7974715b9a43bab207df75ef69209cdf13850 (diff) | |
Add more to css
74 files changed, 2114 insertions, 350 deletions
diff --git a/gemini/index.gmi b/gemini/index.gmi index 88a4810..e3bf57e 100644 --- a/gemini/index.gmi +++ b/gemini/index.gmi @@ -27,22 +27,24 @@ Hello and welcome to my little bit of the internet where I occasionally write ab ## Tags -=> /posts/arch/ Arch. -=> /posts/gemini/ Gemini. -=> /posts/jigsaw/ Jigsaw. -=> /posts/laravel/ Laravel. -=> /posts/linux/ Linux. -=> /posts/markdown/ Markdown. -=> /posts/netlify/ Netlify. -=> /posts/nextcloud/ Nextcloud. -=> /posts/php/ PHP. +=> /posts/arch/ Arch +=> /posts/gemini/ Gemini +=> /posts/jigsaw/ Jigsaw +=> /posts/laravel/ Laravel +=> /posts/linux/ Linux +=> /posts/markdown/ Markdown +=> /posts/netlify/ Netlify +=> /posts/nextcloud/ Nextcloud +=> /posts/php/ PHP ## Where to Find Me -=> https://github.com/davidtsadler/ GitHub. -=> gemini://davidtsadler.com/ Gemini site. +=> https://github.com/davidtsadler/ GitHub +=> gemini://davidtsadler.com/ Gemini Site => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.gmi b/gemini/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.gmi index fc55493..96caffa 100644 --- a/gemini/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.gmi +++ b/gemini/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.gmi @@ -51,6 +51,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.gmi b/gemini/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.gmi index c54caa8..b76b630 100644 --- a/gemini/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.gmi +++ b/gemini/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.gmi @@ -61,6 +61,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.gmi b/gemini/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.gmi index 4f7386d..868059a 100644 --- a/gemini/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.gmi +++ b/gemini/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.gmi @@ -134,6 +134,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.gmi b/gemini/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.gmi index 3615d0b..1cf91c0 100644 --- a/gemini/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.gmi +++ b/gemini/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.gmi @@ -96,6 +96,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.gmi b/gemini/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.gmi index fcd56ab..3fb8f39 100644 --- a/gemini/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.gmi +++ b/gemini/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.gmi @@ -60,6 +60,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.gmi b/gemini/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.gmi index d5c6547..7f5e95b 100644 --- a/gemini/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.gmi +++ b/gemini/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.gmi @@ -110,6 +110,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/arch/index.gmi b/gemini/posts/arch/index.gmi index 52b4756..0e7a55c 100644 --- a/gemini/posts/arch/index.gmi +++ b/gemini/posts/arch/index.gmi @@ -8,6 +8,8 @@ => /posts/arch/2020-06-15/adding-a-user-in-arch-linux/ 2020-06-15 - Adding a User in Arch Linux => /posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/ 2020-05-25 - Installing Arch Linux on a Thinkpad X220 +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.gmi b/gemini/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.gmi index 5e8768c..7cfc137 100644 --- a/gemini/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.gmi +++ b/gemini/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.gmi @@ -299,6 +299,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/gemini/index.gmi b/gemini/posts/gemini/index.gmi index 6efe787..a59d946 100644 --- a/gemini/posts/gemini/index.gmi +++ b/gemini/posts/gemini/index.gmi @@ -2,6 +2,8 @@ => /posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/ 2021-02-08 - How to Host Your Own Gemini Site in the Cloud +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/index.gmi b/gemini/posts/index.gmi index 2aad1f8..635c3bd 100644 --- a/gemini/posts/index.gmi +++ b/gemini/posts/index.gmi @@ -17,6 +17,8 @@ => /posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/ 2020-05-25 - Installing Arch Linux on a Thinkpad X220 => /posts/markdown/2020-03-30/creating-an-ebook-with-markdown/ 2020-03-30 - Creating an Ebook With Markdown +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.gmi b/gemini/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.gmi index 1d4c064..cc10ab7 100644 --- a/gemini/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.gmi +++ b/gemini/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.gmi @@ -42,6 +42,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/jigsaw/index.gmi b/gemini/posts/jigsaw/index.gmi index d0700f2..b729c3e 100644 --- a/gemini/posts/jigsaw/index.gmi +++ b/gemini/posts/jigsaw/index.gmi @@ -2,6 +2,8 @@ => /posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/ 2020-06-01 - Scheduling Posts in Jigsaw +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.gmi b/gemini/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.gmi index 76e4c91..de60dca 100644 --- a/gemini/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.gmi +++ b/gemini/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.gmi @@ -48,6 +48,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.gmi b/gemini/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.gmi index 5fd1807..bc5b769 100644 --- a/gemini/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.gmi +++ b/gemini/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.gmi @@ -308,6 +308,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/laravel/index.gmi b/gemini/posts/laravel/index.gmi index 6ee13bd..14c3d5c 100644 --- a/gemini/posts/laravel/index.gmi +++ b/gemini/posts/laravel/index.gmi @@ -3,6 +3,8 @@ => /posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/ 2020-12-21 - Installing Laravel Homestead in Arch Linux => /posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/ 2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.gmi b/gemini/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.gmi index f34f3f0..685bdac 100644 --- a/gemini/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.gmi +++ b/gemini/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.gmi @@ -40,6 +40,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/linux/index.gmi b/gemini/posts/linux/index.gmi index bb5e57a..0c0c0ab 100644 --- a/gemini/posts/linux/index.gmi +++ b/gemini/posts/linux/index.gmi @@ -2,6 +2,8 @@ => /posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/ 2020-07-13 - Sudo: sorry, you must have a tty to run sudo +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.gmi b/gemini/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.gmi index 25fc38a..46bbf12 100644 --- a/gemini/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.gmi +++ b/gemini/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.gmi @@ -89,6 +89,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/markdown/index.gmi b/gemini/posts/markdown/index.gmi index 94a4717..cb90c79 100644 --- a/gemini/posts/markdown/index.gmi +++ b/gemini/posts/markdown/index.gmi @@ -2,6 +2,8 @@ => /posts/markdown/2020-03-30/creating-an-ebook-with-markdown/ 2020-03-30 - Creating an Ebook With Markdown +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.gmi b/gemini/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.gmi index abda933..0055fe1 100644 --- a/gemini/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.gmi +++ b/gemini/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.gmi @@ -33,6 +33,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/netlify/index.gmi b/gemini/posts/netlify/index.gmi index 3de8bee..5478acf 100644 --- a/gemini/posts/netlify/index.gmi +++ b/gemini/posts/netlify/index.gmi @@ -2,6 +2,8 @@ => /posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/ 2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.gmi b/gemini/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.gmi index 81e25c0..5e2af6f 100644 --- a/gemini/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.gmi +++ b/gemini/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.gmi @@ -40,12 +40,16 @@ Now I can mount Nextcloud and access my files just like any others. $ mount .local/share/nextcloud ``` +### Links + => /posts/nextcloud Nextcloud - Read More Posts. I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to. => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/nextcloud/index.gmi b/gemini/posts/nextcloud/index.gmi index 99d6201..48bec78 100644 --- a/gemini/posts/nextcloud/index.gmi +++ b/gemini/posts/nextcloud/index.gmi @@ -2,6 +2,8 @@ => /posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/ 2021-02-15 - Accessing Nextcloud With WebDAV on Arch +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/php/2021-01-18/installing-php-8-for-windows-10/index.gmi b/gemini/posts/php/2021-01-18/installing-php-8-for-windows-10/index.gmi index 0a9907f..cae5905 100644 --- a/gemini/posts/php/2021-01-18/installing-php-8-for-windows-10/index.gmi +++ b/gemini/posts/php/2021-01-18/installing-php-8-for-windows-10/index.gmi @@ -56,6 +56,8 @@ I don't have comments as I don't want to manage them. You can however contact me => mailto:david@davidtsadler.com Email david@davidtsadler.com +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/gemini/posts/php/index.gmi b/gemini/posts/php/index.gmi index 4e40910..6a66f56 100644 --- a/gemini/posts/php/index.gmi +++ b/gemini/posts/php/index.gmi @@ -2,6 +2,8 @@ => /posts/php/2021-01-18/installing-php-8-for-windows-10/ 2021-01-18 - Installing PHP 8 for Windows 10 +### License + => https://creativecommons.org/licenses/by-sa/4.0/ The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Copyright © 2021 David T. Sadler. diff --git a/html_templates/default.html b/html_templates/default.html index e3f08f8..560fa93 100644 --- a/html_templates/default.html +++ b/html_templates/default.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{{ $title }}</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -19,5 +22,7 @@ </head> <body> <section>{{ $contents }}</section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/scripts/build.php b/scripts/build.php deleted file mode 100644 index e610298..0000000 --- a/scripts/build.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php declare(strict_types=1); - -require_once __DIR__.DIRECTORY_SEPARATOR.'functions.php'; - -$hostname = 'davidtsadler.com'; - -$geminiSrc = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'gemini'; - -if (!is_dir($geminiSrc)) { - echo "Unable to locate gemini directory.\n\n"; - exit(1); -} - -if (!is_readable($geminiSrc)) { - echo "Gemini directory is not readable.\n\n"; - exit(1); -} - -$output = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'www'; - -if (!is_dir($output)) { - echo "Unable to locate output directory.\n\n"; - exit(1); -} - -if (!is_writable($output)) { - echo "Output directory is not writable.\n\n"; - exit(1); -} - -$htmlTemplateDiretory = __DIR__.DIRECTORY_SEPARATOR.'../html_templates'; - -if (!is_dir($htmlTemplateDiretory)) { - echo "Unable to locate html template directory.\n\n"; - exit(1); -} - -if (!is_readable($htmlTemplateDiretory)) { - echo "HTML template directory is not readable.\n\n"; - exit(1); -} - -$pages = getPages($geminiSrc, $output); - -buildWWWSite($pages, $hostname, $htmlTemplateDiretory); - diff --git a/scripts/functions.php b/scripts/functions.php index 3db5744..016b581 100644 --- a/scripts/functions.php +++ b/scripts/functions.php @@ -146,7 +146,7 @@ function gemtext2hmtl(string $gemtext): string $lines = preg_split('/\r?\n/', htmlspecialchars($gemtext)); - $line = fn ($index) => trim($lines[$index]); + $line = fn ($index) => $lines[$index]; $index = 0; diff --git a/www/css/highlight.min.css b/www/css/highlight.min.css new file mode 100644 index 0000000..8fc9a15 --- /dev/null +++ b/www/css/highlight.min.css @@ -0,0 +1 @@ +.hljs{display:block;overflow-x:auto;padding:.5em;background:#f0f0f0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#78a960}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
\ No newline at end of file diff --git a/www/css/modern-normalize.min.css b/www/css/modern-normalize.min.css new file mode 100644 index 0000000..aba965c --- /dev/null +++ b/www/css/modern-normalize.min.css @@ -0,0 +1,9 @@ +/** + * Minified by jsDelivr using clean-css v4.2.3. + * Original file: /npm/modern-normalize@1.1.0/modern-normalize.css + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ +*,::after,::before{box-sizing:border-box}html{-moz-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji'}hr{height:0;color:inherit}abbr[title]{text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item} +/*# sourceMappingURL=/sm/b8ae781793ee4eb86edef3f2a2ac43a5575d063ed8cd287d7553f9f1b2776616.map */
\ No newline at end of file diff --git a/www/css/railscasts.css b/www/css/railscasts.css new file mode 100644 index 0000000..901eb94 --- /dev/null +++ b/www/css/railscasts.css @@ -0,0 +1,175 @@ +/*! + Theme: Railscasts + Author: Ryan Bates (http://railscasts.com) + License: ~ MIT (or more permissive) [via base16-schemes-source] + Maintainer: @highlightjs/core-team + Version: 2021.05.0 +*/ + +/* + WARNING: DO NOT EDIT THIS FILE DIRECTLY. + + This theme file was auto-generated from the Base16 scheme railscasts + by the Highlight.js Base16 template builder. + + - https://github.com/highlightjs/base16-highlightjs +*/ + +/* +base00 #2b2b2b Default Background +base01 #272935 Lighter Background (Used for status bars, line number and folding marks) +base02 #3a4055 Selection Background +base03 #5a647e Comments, Invisibles, Line Highlighting +base04 #d4cfc9 Dark Foreground (Used for status bars) +base05 #e6e1dc Default Foreground, Caret, Delimiters, Operators +base06 #f4f1ed Light Foreground (Not often used) +base07 #f9f7f3 Light Background (Not often used) +base08 #da4939 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted +base09 #cc7833 Integers, Boolean, Constants, XML Attributes, Markup Link Url +base0A #ffc66d Classes, Markup Bold, Search Text Background +base0B #a5c261 Strings, Inherited Class, Markup Code, Diff Inserted +base0C #519f50 Support, Regular Expressions, Escape Characters, Markup Quotes +base0D #6d9cbe Functions, Methods, Attribute IDs, Headings +base0E #b6b3eb Keywords, Storage, Selector, Markup Italic, Diff Changed +base0F #bc9458 Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?> +*/ + +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; +} + +code.hljs { + padding: 3px 5px; +} + +.hljs { + color: #e6e1dc; + background: #2b2b2b; +} + +.hljs ::selection { + color: #3a4055; +} + + +/* purposely do not highlight these things */ +.hljs-formula, +.hljs-params, +.hljs-property +{} + +/* base03 - #5a647e - Comments, Invisibles, Line Highlighting */ +.hljs-comment { + color: #5a647e; +} + +/* base04 - #d4cfc9 - Dark Foreground (Used for status bars) */ +.hljs-tag { + color: #d4cfc9; +} + +/* base05 - #e6e1dc - Default Foreground, Caret, Delimiters, Operators */ +.hljs-subst, +.hljs-punctuation, +.hljs-operator { + color: #e6e1dc; +} + +.hljs-operator { + opacity: 0.7; +} + +/* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ +.hljs-bullet, +.hljs-variable, +.hljs-template-variable, +.hljs-selector-tag, +.hljs-name, +.hljs-deletion { + color: #da4939; +} + +/* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ +.hljs-symbol, +.hljs-number, +.hljs-link, +.hljs-attr, +.hljs-variable.constant_, +.hljs-literal { + color: #cc7833; +} + +/* base0A - Classes, Markup Bold, Search Text Background */ +.hljs-title, +.hljs-class .hljs-title, +.hljs-title.class_ +{ + color: #ffc66d; +} + +.hljs-strong { + font-weight:bold; + color: #ffc66d; +} + +/* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ +.hljs-code, +.hljs-addition, +.hljs-title.class_.inherited__, +.hljs-string { + color: #a5c261; +} + +/* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ +.hljs-built_in, +.hljs-doctag, /* guessing */ +.hljs-quote, +.hljs-keyword.hljs-atrule, +.hljs-regexp { + color: #519f50; +} + +/* base0D - Functions, Methods, Attribute IDs, Headings */ +.hljs-function .hljs-title, +.hljs-attribute, +.ruby .hljs-property, +.hljs-title.function_, +.hljs-section { + color: #6d9cbe; +} + +/* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ +.hljs-type, +/* .hljs-selector-id, */ +/* .hljs-selector-class, */ +/* .hljs-selector-attr, */ +/* .hljs-selector-pseudo, */ +.hljs-template-tag, +.diff .hljs-meta, +.hljs-keyword { + color: #b6b3eb; +} +.hljs-emphasis { + color: #b6b3eb; + font-style: italic; +} + +/* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?> */ +.hljs-meta, +/* + prevent top level .keyword and .string scopes + from leaking into meta by accident +*/ +.hljs-meta .hljs-keyword, +.hljs-meta .hljs-string +{ + color: #bc9458; +} + +.hljs-meta .hljs-keyword, +/* for v10 compatible themes */ +.hljs-meta-keyword { + font-weight: bold; +} diff --git a/www/css/site.css b/www/css/site.css index 8e80eca..9914445 100644 --- a/www/css/site.css +++ b/www/css/site.css @@ -1,3 +1,75 @@ -section a { +@font-face { + font-family: 'comicsans'; + src: url('/fonts/ComicMono.ttf') format('truetype'), + url("/fonts/ComicMono.ttf") format("truetype"); +} + +body { + background-color: #1a202c; + font-family: "comicsans", monospace; + color: #ffffff; + font-size: 1.125rem; + line-height: 1.75rem; +} + +h1, +h2, +h3 { + font-weight: bold; + font-size: 1.125rem; +} + +h1 { + color: #BEF264; + text-align: center; + width: 100%; +} + +h2 { + color: #FCD34D; +} + +h2:before { + content: "## "; +} + +h3 { + color: #86EFAC; +} + +h3:before { + content: "### "; +} + + +section { + padding: 1rem; +} + +blockquote { + margin: 0; + font-weight: bold; +} + +a { display: block; + color: #93C5FD; + text-decoration: none; +} + +a:hover { + color: #3730A3; +} + +a:before { + content: "=> "; +} + +pre { + overflow-x: auto; +} + +code.hljs { + background-color: #232323; + font-size: 1.125rem; } diff --git a/www/fonts/ComicMono-Bold.ttf b/www/fonts/ComicMono-Bold.ttf Binary files differnew file mode 100644 index 0000000..e03f41e --- /dev/null +++ b/www/fonts/ComicMono-Bold.ttf diff --git a/www/fonts/ComicMono.ttf b/www/fonts/ComicMono.ttf Binary files differnew file mode 100644 index 0000000..9bc7354 --- /dev/null +++ b/www/fonts/ComicMono.ttf diff --git a/www/index.html b/www/index.html index 90fb068..51c693e 100644 --- a/www/index.html +++ b/www/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler</h1><p>Hello and welcome to my little bit of the internet where I occasionally write about things that interest me. You might find my posts interesting or you might not and that's okay.</p><h2>Latest Posts</h2><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><h2>All Posts</h2><a href="/posts/">Post Archive</a><h2>Tags</h2><a href="/posts/arch/">Arch.</a><a href="/posts/gemini/">Gemini.</a><a href="/posts/jigsaw/">Jigsaw.</a><a href="/posts/laravel/">Laravel.</a><a href="/posts/linux/">Linux.</a><a href="/posts/markdown/">Markdown.</a><a href="/posts/netlify/">Netlify.</a><a href="/posts/nextcloud/">Nextcloud.</a><a href="/posts/php/">PHP.</a><h2>Where to Find Me</h2><a href="https://github.com/davidtsadler/">GitHub.</a><a href="gemini://davidtsadler.com/">Gemini site.</a><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p></section> + <section><h1>The Home of David T. Sadler</h1><p>Hello and welcome to my little bit of the internet where I occasionally write about things that interest me. You might find my posts interesting or you might not and that's okay.</p><h2>Latest Posts</h2><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><h2>All Posts</h2><a href="/posts/">Post Archive</a><h2>Tags</h2><a href="/posts/arch/">Arch</a><a href="/posts/gemini/">Gemini</a><a href="/posts/jigsaw/">Jigsaw</a><a href="/posts/laravel/">Laravel</a><a href="/posts/linux/">Linux</a><a href="/posts/markdown/">Markdown</a><a href="/posts/netlify/">Netlify</a><a href="/posts/nextcloud/">Nextcloud</a><a href="/posts/php/">PHP</a><h2>Where to Find Me</h2><a href="https://github.com/davidtsadler/">GitHub</a><a href="gemini://davidtsadler.com/">Gemini Site</a><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/js/highlight.min.js b/www/js/highlight.min.js new file mode 100644 index 0000000..ffeb034 --- /dev/null +++ b/www/js/highlight.min.js @@ -0,0 +1,1358 @@ +/* + Highlight.js 10.7.2 (00233d63) + License: BSD-3-Clause + Copyright (c) 2006-2021, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var i=t[n] +;"object"!=typeof i||Object.isFrozen(i)||e(i)})),t}var t=e,n=e;t.default=n +;class i{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function s(e){ +return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const r=e=>!!e.kind +;class l{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=s(e)}openNode(e){if(!r(e))return;let t=e.kind +;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){ +r(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){ +this.buffer+=`<span class="${e}">`}}class o{constructor(){this.rootNode={ +children:[]},this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class c extends o{constructor(e){super(),this.options=e} +addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} +addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root +;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ +return new l(this,this.options).value()}finalize(){return!0}}function g(e){ +return e?"string"==typeof e?e:e.source:null} +const u=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,h="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",f="\\b\\d+(\\.\\d+)?",p="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",m="\\b(0b[01]+)",b={ +begin:"\\\\[\\s\\S]",relevance:0},E={className:"string",begin:"'",end:"'", +illegal:"\\n",contains:[b]},x={className:"string",begin:'"',end:'"', +illegal:"\\n",contains:[b]},v={ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},w=(e,t,n={})=>{const i=a({className:"comment",begin:e,end:t,contains:[]},n) +;return i.contains.push(v),i.contains.push({className:"doctag", +begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),i +},y=w("//","$"),N=w("/\\*","\\*/"),R=w("#","$");var _=Object.freeze({ +__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:h,UNDERSCORE_IDENT_RE:d, +NUMBER_RE:f,C_NUMBER_RE:p,BINARY_NUMBER_RE:m, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=((...e)=>e.map((e=>g(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)), +a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{ +0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:b,APOS_STRING_MODE:E, +QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:v,COMMENT:w,C_LINE_COMMENT_MODE:y, +C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number", +begin:f,relevance:0},C_NUMBER_MODE:{className:"number",begin:p,relevance:0}, +BINARY_NUMBER_MODE:{className:"number",begin:m,relevance:0},CSS_NUMBER_MODE:{ +className:"number", +begin:f+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp", +begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[b,{begin:/\[/,end:/\]/, +relevance:0,contains:[b]}]}]},TITLE_MODE:{className:"title",begin:h,relevance:0 +},UNDERSCORE_TITLE_MODE:{className:"title",begin:d,relevance:0},METHOD_GUARD:{ +begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function k(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function M(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=k,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function O(e,t){ +Array.isArray(e.illegal)&&(e.illegal=((...e)=>"("+e.map((e=>g(e))).join("|")+")")(...e.illegal)) +}function A(e,t){if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function L(e,t){ +void 0===e.relevance&&(e.relevance=1)} +const I=["of","and","for","in","not","or","if","then","parent","list","value"] +;function j(e,t,n="keyword"){const i={} +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,j(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,B(n[0],n[1])]}))}}function B(e,t){ +return t?Number(t):(e=>I.includes(e.toLowerCase()))(e)?0:1} +function T(e,{plugins:t}){function n(t,n){ +return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class i{ +constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){ +0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(((e,t="|")=>{let n=0 +;return e.map((e=>{n+=1;const t=n;let i=g(e),s="";for(;i.length>0;){ +const e=u.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index),i=i.substring(e.index+e[0].length), +"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],"("===e[0]&&n++)}return s +})).map((e=>`(${e})`)).join(t)})(e),!0),this.lastIndex=0}exec(e){ +this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e) +;if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new i +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(i,r){const l=i +;if(i.isCompiled)return l +;[A].forEach((e=>e(i,r))),e.compilerExtensions.forEach((e=>e(i,r))), +i.__beforeBegin=null,[M,O,L].forEach((e=>e(i,r))),i.isCompiled=!0;let o=null +;if("object"==typeof i.keywords&&(o=i.keywords.$pattern, +delete i.keywords.$pattern), +i.keywords&&(i.keywords=j(i.keywords,e.case_insensitive)), +i.lexemes&&o)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ") +;return o=o||i.lexemes||/\w+/, +l.keywordPatternRe=n(o,!0),r&&(i.begin||(i.begin=/\B|\b/), +l.beginRe=n(i.begin),i.endSameAsBegin&&(i.end=i.begin), +i.end||i.endsWithParent||(i.end=/\B|\b/), +i.end&&(l.endRe=n(i.end)),l.terminatorEnd=g(i.end)||"", +i.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(i.end?"|":"")+r.terminatorEnd)), +i.illegal&&(l.illegalRe=n(i.illegal)), +i.contains||(i.contains=[]),i.contains=[].concat(...i.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>a(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:S(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?i:e)))),i.contains.forEach((e=>{t(e,l) +})),i.starts&&t(i.starts,r),l.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(l),l}(e)}function S(e){ +return!!e&&(e.endsWithParent||S(e.starts))}function P(e){const t={ +props:["language","code","autodetect"],data:()=>({detectedLanguage:"", +unknownLanguage:!1}),computed:{className(){ +return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){ +if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`), +this.unknownLanguage=!0,s(this.code);let t={} +;return this.autoDetect?(t=e.highlightAuto(this.code), +this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals), +this.detectedLanguage=this.language),t.value},autoDetect(){ +return!(this.language&&(e=this.autodetect,!e&&""!==e));var e}, +ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{ +class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{ +Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const D={ +"after:highlightElement":({el:e,result:t,text:n})=>{const i=H(e) +;if(!i.length)return;const a=document.createElement("div") +;a.innerHTML=t.value,t.value=((e,t,n)=>{let i=0,a="";const r=[];function l(){ +return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset<t[0].offset?e:t:"start"===t[0].event?e:t:e.length?e:t +}function o(e){a+="<"+C(e)+[].map.call(e.attributes,(function(e){ +return" "+e.nodeName+'="'+s(e.value)+'"'})).join("")+">"}function c(e){ +a+="</"+C(e)+">"}function g(e){("start"===e.event?o:c)(e.node)} +for(;e.length||t.length;){let t=l() +;if(a+=s(n.substring(i,t[0].offset)),i=t[0].offset,t===e){r.reverse().forEach(c) +;do{g(t.splice(0,1)[0]),t=l()}while(t===e&&t.length&&t[0].offset===i) +;r.reverse().forEach(o) +}else"start"===t[0].event?r.push(t[0].node):r.pop(),g(t.splice(0,1)[0])} +return a+s(n.substr(i))})(i,H(a),n)}};function C(e){ +return e.nodeName.toLowerCase()}function H(e){const t=[];return function e(n,i){ +for(let s=n.firstChild;s;s=s.nextSibling)3===s.nodeType?i+=s.nodeValue.length:1===s.nodeType&&(t.push({ +event:"start",offset:i,node:s}),i=e(s,i),C(s).match(/br|hr|img|input/)||t.push({ +event:"stop",offset:i,node:s}));return i}(e,0),t}const $={},U=e=>{ +console.error(e)},z=(e,...t)=>{console.log("WARN: "+e,...t)},K=(e,t)=>{ +$[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),$[`${e}/${t}`]=!0) +},G=s,V=a,W=Symbol("nomatch");return(e=>{ +const n=Object.create(null),s=Object.create(null),a=[];let r=!0 +;const l=/(^(<[^>]+>|\t|)+|\n)/gm,o="Could not find the language '{}', did you forget to load/include a language module?",g={ +disableAutodetect:!0,name:"Plain text",contains:[]};let u={ +noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +tabReplace:null,useBR:!1,languages:null,__emitter:c};function h(e){ +return u.noHighlightRe.test(e)}function d(e,t,n,i){let s="",a="" +;"object"==typeof t?(s=e, +n=t.ignoreIllegals,a=t.language,i=void 0):(K("10.7.0","highlight(lang, code, ...args) has been deprecated."), +K("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +a=e,s=t);const r={code:s,language:a};M("before:highlight",r) +;const l=r.result?r.result:f(r.language,r.code,n,i) +;return l.code=r.code,M("after:highlight",l),l}function f(e,t,s,l){ +function c(e,t){const n=v.case_insensitive?t[0].toLowerCase():t[0] +;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]} +function g(){null!=R.subLanguage?(()=>{if(""===M)return;let e=null +;if("string"==typeof R.subLanguage){ +if(!n[R.subLanguage])return void k.addText(M) +;e=f(R.subLanguage,M,!0,_[R.subLanguage]),_[R.subLanguage]=e.top +}else e=p(M,R.subLanguage.length?R.subLanguage:null) +;R.relevance>0&&(O+=e.relevance),k.addSublanguage(e.emitter,e.language) +})():(()=>{if(!R.keywords)return void k.addText(M);let e=0 +;R.keywordPatternRe.lastIndex=0;let t=R.keywordPatternRe.exec(M),n="";for(;t;){ +n+=M.substring(e,t.index);const i=c(R,t);if(i){const[e,s]=i +;if(k.addText(n),n="",O+=s,e.startsWith("_"))n+=t[0];else{ +const n=v.classNameAliases[e]||e;k.addKeyword(t[0],n)}}else n+=t[0] +;e=R.keywordPatternRe.lastIndex,t=R.keywordPatternRe.exec(M)} +n+=M.substr(e),k.addText(n)})(),M=""}function h(e){ +return e.className&&k.openNode(v.classNameAliases[e.className]||e.className), +R=Object.create(e,{parent:{value:R}}),R}function d(e,t,n){let s=((e,t)=>{ +const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(s){if(e["on:end"]){ +const n=new i(e);e["on:end"](t,n),n.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return d(e.parent,t,n)}function m(e){ +return 0===R.matcher.regexIndex?(M+=e[0],1):(I=!0,0)}function b(e){ +const n=e[0],i=t.substr(e.index),s=d(R,e,i);if(!s)return W;const a=R +;a.skip?M+=n:(a.returnEnd||a.excludeEnd||(M+=n),g(),a.excludeEnd&&(M=n));do{ +R.className&&k.closeNode(),R.skip||R.subLanguage||(O+=R.relevance),R=R.parent +}while(R!==s.parent) +;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe), +h(s.starts)),a.returnEnd?0:n.length}let E={};function x(n,a){const l=a&&a[0] +;if(M+=n,null==l)return g(),0 +;if("begin"===E.type&&"end"===a.type&&E.index===a.index&&""===l){ +if(M+=t.slice(a.index,a.index+1),!r){const t=Error("0 width match regex") +;throw t.languageName=e,t.badRule=E.rule,t}return 1} +if(E=a,"begin"===a.type)return function(e){ +const t=e[0],n=e.rule,s=new i(n),a=[n.__beforeBegin,n["on:begin"]] +;for(const n of a)if(n&&(n(e,s),s.isMatchIgnored))return m(t) +;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")), +n.skip?M+=t:(n.excludeBegin&&(M+=t), +g(),n.returnBegin||n.excludeBegin||(M=t)),h(n),n.returnBegin?0:t.length}(a) +;if("illegal"===a.type&&!s){ +const e=Error('Illegal lexeme "'+l+'" for mode "'+(R.className||"<unnamed>")+'"') +;throw e.mode=R,e}if("end"===a.type){const e=b(a);if(e!==W)return e} +if("illegal"===a.type&&""===l)return 1 +;if(L>1e5&&L>3*a.index)throw Error("potential infinite loop, way more iterations than matches") +;return M+=l,l.length}const v=N(e) +;if(!v)throw U(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const w=T(v,{plugins:a});let y="",R=l||w;const _={},k=new u.__emitter(u);(()=>{ +const e=[];for(let t=R;t!==v;t=t.parent)t.className&&e.unshift(t.className) +;e.forEach((e=>k.openNode(e)))})();let M="",O=0,A=0,L=0,I=!1;try{ +for(R.matcher.considerAll();;){ +L++,I?I=!1:R.matcher.considerAll(),R.matcher.lastIndex=A +;const e=R.matcher.exec(t);if(!e)break;const n=x(t.substring(A,e.index),e) +;A=e.index+n}return x(t.substr(A)),k.closeAllNodes(),k.finalize(),y=k.toHTML(),{ +relevance:Math.floor(O),value:y,language:e,illegal:!1,emitter:k,top:R}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{ +msg:n.message,context:t.slice(A-100,A+100),mode:n.mode},sofar:y,relevance:0, +value:G(t),emitter:k};if(r)return{illegal:!1,relevance:0,value:G(t),emitter:k, +language:e,top:R,errorRaised:n};throw n}}function p(e,t){ +t=t||u.languages||Object.keys(n);const i=(e=>{const t={relevance:0, +emitter:new u.__emitter(u),value:G(e),illegal:!1,top:g} +;return t.emitter.addText(e),t})(e),s=t.filter(N).filter(k).map((t=>f(t,e,!1))) +;s.unshift(i);const a=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(N(e.language).supersetOf===t.language)return 1 +;if(N(t.language).supersetOf===e.language)return-1}return 0})),[r,l]=a,o=r +;return o.second_best=l,o}const m={"before:highlightElement":({el:e})=>{ +u.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,"\n")) +},"after:highlightElement":({result:e})=>{ +u.useBR&&(e.value=e.value.replace(/\n/g,"<br>"))}},b=/^(<[^>]+>|\t)+/gm,E={ +"after:highlightElement":({result:e})=>{ +u.tabReplace&&(e.value=e.value.replace(b,(e=>e.replace(/\t/g,u.tabReplace))))}} +;function x(e){let t=null;const n=(e=>{let t=e.className+" " +;t+=e.parentNode?e.parentNode.className:"";const n=u.languageDetectRe.exec(t) +;if(n){const t=N(n[1]) +;return t||(z(o.replace("{}",n[1])),z("Falling back to no-highlight mode for this block.",e)), +t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||N(e)))})(e) +;if(h(n))return;M("before:highlightElement",{el:e,language:n}),t=e +;const i=t.textContent,a=n?d(i,{language:n,ignoreIllegals:!0}):p(i) +;M("after:highlightElement",{el:e,result:a,text:i +}),e.innerHTML=a.value,((e,t,n)=>{const i=t?s[t]:n +;e.classList.add("hljs"),i&&e.classList.add(i)})(e,n,a.language),e.result={ +language:a.language,re:a.relevance,relavance:a.relevance +},a.second_best&&(e.second_best={language:a.second_best.language, +re:a.second_best.relevance,relavance:a.second_best.relevance})}const v=()=>{ +v.called||(v.called=!0, +K("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead."), +document.querySelectorAll("pre code").forEach(x))};let w=!1;function y(){ +"loading"!==document.readyState?document.querySelectorAll("pre code").forEach(x):w=!0 +}function N(e){return e=(e||"").toLowerCase(),n[e]||n[s[e]]} +function R(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=N(e) +;return t&&!t.disableAutodetect}function M(e,t){const n=e;a.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +w&&y()}),!1),Object.assign(e,{highlight:d,highlightAuto:p,highlightAll:y, +fixMarkup:e=>{ +return K("10.2.0","fixMarkup will be removed entirely in v11.0"),K("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"), +t=e, +u.tabReplace||u.useBR?t.replace(l,(e=>"\n"===e?u.useBR?"<br>":e:u.tabReplace?e.replace(/\t/g,u.tabReplace):e)):t +;var t},highlightElement:x, +highlightBlock:e=>(K("10.7.0","highlightBlock will be removed entirely in v12.0"), +K("10.7.0","Please use highlightElement now."),x(e)),configure:e=>{ +e.useBR&&(K("10.3.0","'useBR' will be removed entirely in v11.0"), +K("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")), +u=V(u,e)},initHighlighting:v,initHighlightingOnLoad:()=>{ +K("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."), +w=!0},registerLanguage:(t,i)=>{let s=null;try{s=i(e)}catch(e){ +if(U("Language definition for '{}' could not be registered.".replace("{}",t)), +!r)throw e;U(e),s=g} +s.name||(s.name=t),n[t]=s,s.rawDefinition=i.bind(null,e),s.aliases&&R(s.aliases,{ +languageName:t})},unregisterLanguage:e=>{delete n[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(n),getLanguage:N,registerAliases:R, +requireLanguage:e=>{ +K("10.4.0","requireLanguage will be removed entirely in v11."), +K("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844") +;const t=N(e);if(t)return t +;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))}, +autoDetection:k,inherit:V,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),a.push(e)}, +vuePlugin:P(e).VuePlugin}),e.debugMode=()=>{r=!1},e.safeMode=()=>{r=!0 +},e.versionString="10.7.2";for(const e in _)"object"==typeof _[e]&&t(_[e]) +;return Object.assign(e,_),e.addPlugin(m),e.addPlugin(D),e.addPlugin(E),e})({}) +}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); +hljs.registerLanguage("apache",(()=>{"use strict";return e=>{const n={ +className:"number",begin:/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/} +;return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0, +contains:[e.HASH_COMMENT_MODE,{className:"section",begin:/<\/?/,end:/>/, +contains:[n,{className:"number",begin:/:\d{1,5}/ +},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute", +begin:/\w+/,relevance:0,keywords:{ +nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername" +},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"}, +contains:[{className:"meta",begin:/\s\[/,end:/\]$/},{className:"variable", +begin:/[\$%]\{/,end:/\}/,contains:["self",{className:"number",begin:/[$%]\d+/}] +},n,{className:"number",begin:/\d+/},e.QUOTE_STRING_MODE]}}],illegal:/\S/}} +})()); +hljs.registerLanguage("bash",(()=>{"use strict";function e(...e){ +return e.map((e=>{return(s=e)?"string"==typeof s?s:s.source:null;var s +})).join("")}return s=>{const n={},t={begin:/\$\{/,end:/\}/,contains:["self",{ +begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{ +begin:e(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},t]});const a={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},i={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},c={className:"string",begin:/"/,end:/"/, +contains:[s.BACKSLASH_ESCAPE,n,a]};a.contains.push(c);const o={begin:/\$\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,n] +},r=s.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),l={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/, +keyword:"if then else elif fi for while in do done case esac function", +literal:"true false", +built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp" +},contains:[r,s.SHEBANG(),l,o,s.HASH_COMMENT_MODE,i,c,{className:"",begin:/\\"/ +},{className:"string",begin:/'/,end:/'/},n]}}})()); +hljs.registerLanguage("c",(()=>{"use strict";function e(e){ +return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?") +}return t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",i={ +className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string", +variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", +contains:[t.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},t.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},t.inherit(s,{className:"meta-string"}),{ +className:"meta-string",begin:/<.*?>/},n,t.C_BLOCK_COMMENT_MODE]},l={ +className:"title",begin:e(r)+t.IDENT_RE,relevance:0 +},d=e(r)+t.IDENT_RE+"\\s*\\(",u={ +keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", +literal:"true false nullptr NULL"},m=[c,i,n,t.C_BLOCK_COMMENT_MODE,o,s],p={ +variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ +beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:m.concat([{ +begin:/\(/,end:/\)/,keywords:u,contains:m.concat(["self"]),relevance:0}]), +relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d, +returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/, +contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d, +returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,s,o,i,{ +begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:["self",n,t.C_BLOCK_COMMENT_MODE,s,o,i]}] +},i,n,t.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"</",contains:[].concat(p,_,m,[c,{ +begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<", +end:">",keywords:u,contains:["self",i]},{begin:t.IDENT_RE+"::",keywords:u},{ +className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, +contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{ +preprocessor:c,strings:s,keywords:u}}}})()); +hljs.registerLanguage("coffeescript",(()=>{"use strict" +;const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) +;return r=>{const t={ +keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((i=["var","const","let","function","static"], +e=>!i.includes(e))),literal:n.concat(["yes","no","on","off"]), +built_in:a.concat(["npm","print"])};var i;const s="[A-Za-z$_][0-9A-Za-z$_]*",o={ +className:"subst",begin:/#\{/,end:/\}/,keywords:t +},c=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?", +relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/, +contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE] +},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,o]},{begin:/"/,end:/"/, +contains:[r.BACKSLASH_ESCAPE,o]}]},{className:"regexp",variants:[{begin:"///", +end:"///",contains:[o,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)", +relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+s +},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{ +begin:"```",end:"```"},{begin:"`",end:"`"}]}];o.contains=c +;const l=r.inherit(r.TITLE_MODE,{begin:s}),d="(\\(.*\\)\\s*)?\\B[-=]>",g={ +className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/, +end:/\)/,keywords:t,contains:["self"].concat(c)}]};return{name:"CoffeeScript", +aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/, +contains:c.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{ +className:"function",begin:"^\\s*"+s+"\\s*=\\s*"+d,end:"[-=]>",returnBegin:!0, +contains:[l,g]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function", +begin:d,end:"[-=]>",returnBegin:!0,contains:[g]}]},{className:"class", +beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{ +beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[l]},l] +},{begin:s+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}})()); +hljs.registerLanguage("cpp",(()=>{"use strict";function e(e){ +return t("(",e,")?")}function t(...e){return e.map((e=>{ +return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}return n=>{ +const r=n.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="[a-zA-Z_]\\w*::",i="(decltype\\(auto\\)|"+e(a)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={ +className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string", +variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", +contains:[n.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},n.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},n.inherit(c,{className:"meta-string"}),{ +className:"meta-string",begin:/<.*?>/},r,n.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:e(a)+n.IDENT_RE,relevance:0 +},u=e(a)+n.IDENT_RE+"\\s*\\(",m={ +keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", +built_in:"_Bool _Complex _Imaginary", +_relevance_hints:["asin","atan2","atan","calloc","ceil","cosh","cos","exit","exp","fabs","floor","fmod","fprintf","fputs","free","frexp","auto_ptr","deque","list","queue","stack","vector","map","set","pair","bitset","multiset","multimap","unordered_set","fscanf","future","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","tolower","toupper","labs","ldexp","log10","log","malloc","realloc","memchr","memcmp","memcpy","memset","modf","pow","printf","putchar","puts","scanf","sinh","sin","snprintf","sprintf","sqrt","sscanf","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","tanh","tan","unordered_map","unordered_multiset","unordered_multimap","priority_queue","make_pair","array","shared_ptr","abort","terminate","abs","acos","vfprintf","vprintf","vsprintf","endl","initializer_list","unique_ptr","complex","imaginary","std","string","wstring","cin","cout","cerr","clog","stdin","stdout","stderr","stringstream","istringstream","ostringstream"], +literal:"true false nullptr NULL"},p={className:"function.dispatch",relevance:0, +keywords:m, +begin:t(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!while)/,n.IDENT_RE,(_=/\s*\(/, +t("(?=",_,")")))};var _;const g=[p,l,s,r,n.C_BLOCK_COMMENT_MODE,o,c],b={ +variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ +beginKeywords:"new throw return else",end:/;/}],keywords:m,contains:g.concat([{ +begin:/\(/,end:/\)/,keywords:m,contains:g.concat(["self"]),relevance:0}]), +relevance:0},f={className:"function",begin:"("+i+"[\\*&\\s]+)+"+u, +returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:m,illegal:/[^\w\s\*&:<>.]/, +contains:[{begin:"decltype\\(auto\\)",keywords:m,relevance:0},{begin:u, +returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{begin:/:/, +endsWithParent:!0,contains:[c,o]},{className:"params",begin:/\(/,end:/\)/, +keywords:m,relevance:0,contains:[r,n.C_BLOCK_COMMENT_MODE,c,o,s,{begin:/\(/, +end:/\)/,keywords:m,relevance:0,contains:["self",r,n.C_BLOCK_COMMENT_MODE,c,o,s] +}]},s,r,n.C_BLOCK_COMMENT_MODE,l]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:m,illegal:"</", +classNameAliases:{"function.dispatch":"built_in"}, +contains:[].concat(b,f,p,g,[l,{ +begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<", +end:">",keywords:m,contains:["self",s]},{begin:n.IDENT_RE+"::",keywords:m},{ +className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, +contains:[{beginKeywords:"final class struct"},n.TITLE_MODE]}]),exports:{ +preprocessor:l,strings:c,keywords:m}}}})()); +hljs.registerLanguage("csharp",(()=>{"use strict";return e=>{const n={ +keyword:["abstract","as","base","break","case","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},a=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},t=e.inherit(s,{illegal:/\n/}),r={className:"subst",begin:/\{/,end:/\}/, +keywords:n},l=e.inherit(r,{illegal:/\n/}),c={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,l]},o={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},r]},d=e.inherit(o,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},l]}) +;r.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.C_BLOCK_COMMENT_MODE], +l.contains=[d,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},a] +},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:"</?",end:">"}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,i,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[a,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"meta-string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,i,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}})()); +hljs.registerLanguage("css",(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],r=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse() +;return n=>{const a=(e=>({IMPORTANT:{className:"meta",begin:"!important"}, +HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"}, +ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/, +illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]} +}))(n),l=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS", +case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"}, +classNameAliases:{keyframePosition:"selector-tag"}, +contains:[n.C_BLOCK_COMMENT_MODE,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},n.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},a.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+i.join("|")+")"},{begin:"::("+o.join("|")+")"}]},{ +className:"attribute",begin:"\\b("+r.join("|")+")\\b"},{begin:":",end:"[;}]", +contains:[a.HEXCOLOR,a.IMPORTANT,n.CSS_NUMBER_MODE,...l,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}] +},{className:"built_in",begin:/[\w-]+(?=\()/}]},{ +begin:(s=/@/,((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?=",s,")")), +end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword", +begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0, +relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:t.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute" +},...l,n.CSS_NUMBER_MODE]}]},{className:"selector-tag", +begin:"\\b("+e.join("|")+")\\b"}]};var s}})()); +hljs.registerLanguage("diff",(()=>{"use strict";return e=>({name:"Diff", +aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{ +begin:/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{ +begin:/^--- +\d+,\d+ +----$/}]},{className:"comment",variants:[{begin:/Index: /, +end:/$/},{begin:/^index/,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^-{3}/,end:/$/ +},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/},{ +begin:/^diff --git/,end:/$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]})})()); +hljs.registerLanguage("go",(()=>{"use strict";return e=>{const n={ +keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune", +literal:"true false iota nil", +built_in:"append cap close complex copy imag len make new panic print println real recover delete" +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"</", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string", +variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:"`",end:"`"}]},{ +className:"number",variants:[{begin:e.C_NUMBER_RE+"[i]",relevance:1 +},e.C_NUMBER_MODE]},{begin:/:=/},{className:"function",beginKeywords:"func", +end:"\\s*(\\{|$)",excludeEnd:!0,contains:[e.TITLE_MODE,{className:"params", +begin:/\(/,end:/\)/,keywords:n,illegal:/["']/}]}]}}})()); +hljs.registerLanguage("http",(()=>{"use strict";function e(...e){ +return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n +})).join("")}return n=>{const a="HTTP/(2|1\\.[01])",s={className:"attribute", +begin:e("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{ +className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]} +},t=[s,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{ +name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+a+" \\d{3})", +end:/$/,contains:[{className:"meta",begin:a},{className:"number", +begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:t}},{ +begin:"(?=^[A-Z]+ (.*?) "+a+"$)",end:/$/,contains:[{className:"string", +begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:a},{ +className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:t} +},n.inherit(s,{relevance:0})]}}})()); +hljs.registerLanguage("ini",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(...n){ +return n.map((n=>e(n))).join("")}return s=>{const a={className:"number", +relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:s.NUMBER_RE}] +},i=s.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const t={ +className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/ +}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={ +className:"string",contains:[s.BACKSLASH_ESCAPE],variants:[{begin:"'''", +end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"' +},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,a,"self"], +relevance:0 +},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map((n=>e(n))).join("|")+")" +;return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr", +starts:{end:/$/,contains:[i,c,r,t,l,a]}}]}}})()); +hljs.registerLanguage("java",(()=>{"use strict" +;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ +className:"number",variants:[{ +begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` +},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ +begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` +},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};return e=>{ +var n="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s={ +className:"meta",begin:"@[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*", +contains:[{begin:/\(/,end:/\)/,contains:["self"]}]};const r=a;return{ +name:"Java",aliases:["jsp"],keywords:n,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +className:"class",beginKeywords:"class interface enum",end:/[{;=]/, +excludeEnd:!0,relevance:1,keywords:"class interface enum",illegal:/[:"\[\]]/, +contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{className:"class", +begin:"record\\s+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,excludeEnd:!0, +end:/[{;=]/,keywords:n,contains:[{beginKeywords:"record"},{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, +keywords:n,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"function", +begin:"([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(", +returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, +keywords:n,relevance:0, +contains:[s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,r,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},r,s]}}})()); +hljs.registerLanguage("javascript",(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) +;function r(e){return t("(?=",e,")")}function t(...e){return e.map((e=>{ +return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return i=>{ +const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/, +isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a] +;"<"!==s?">"===s&&(((e,{after:n})=>{const a="</"+e[0].slice(1) +;return-1!==e.input.indexOf(a,n)})(e,{after:a +})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n,literal:a, +built_in:s},g="\\.([0-9](_?[0-9])*)",b="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",d={ +className:"number",variants:[{ +begin:`(\\b(${b})((${g})|\\.)?|(${g}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{ +begin:`\\b(${b})\\b((${g})\\b|\\.)?|(${g})\\b`},{ +begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{ +begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{ +begin:"\\b0[0-7]+n?\\b"}],relevance:0},E={className:"subst",begin:"\\$\\{", +end:"\\}",keywords:l,contains:[]},u={begin:"html`",end:"",starts:{end:"`", +returnEnd:!1,contains:[i.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},_={ +begin:"css`",end:"",starts:{end:"`",returnEnd:!1, +contains:[i.BACKSLASH_ESCAPE,E],subLanguage:"css"}},m={className:"string", +begin:"`",end:"`",contains:[i.BACKSLASH_ESCAPE,E]},y={className:"comment", +variants:[i.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{ +className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{", +end:"\\}",relevance:0},{className:"variable",begin:c+"(?=\\s*(-)|$)", +endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}] +}),i.C_BLOCK_COMMENT_MODE,i.C_LINE_COMMENT_MODE] +},N=[i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,u,_,m,d,i.REGEXP_MODE] +;E.contains=N.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(N) +});const A=[].concat(y,E.contains),f=A.concat([{begin:/\(/,end:/\)/,keywords:l, +contains:["self"].concat(A)}]),p={className:"params",begin:/\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f};return{name:"Javascript", +aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:f}, +illegal:/#(?![$_A-z])/,contains:[i.SHEBANG({label:"shebang",binary:"node", +relevance:5}),{label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,u,_,m,y,d,{ +begin:t(/[{,\n]\s*/,r(t(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))), +relevance:0,contains:[{className:"attr",begin:c+r("\\s*:"),relevance:0}]},{ +begin:"("+i.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",contains:[y,i.REGEXP_MODE,{className:"function", +begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+i.UNDERSCORE_IDENT_RE+")\\s*=>", +returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ +begin:i.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 +},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f}]}] +},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ +variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, +end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, +contains:["self"]}]}],relevance:0},{className:"function", +beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, +contains:["self",i.inherit(i.TITLE_MODE,{begin:c}),p],illegal:/%/},{ +beginKeywords:"while if switch catch for"},{className:"function", +begin:i.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,contains:[p,i.inherit(i.TITLE_MODE,{begin:c})]},{variants:[{ +begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", +beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ +beginKeywords:"extends"},i.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, +end:/[{;]/,excludeEnd:!0,contains:[i.inherit(i.TITLE_MODE,{begin:c}),"self",p] +},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", +contains:[i.inherit(i.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}] +}}})()); +hljs.registerLanguage("json",(()=>{"use strict";return n=>{const e={ +literal:"true false null" +},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],a=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],l={ +end:",",endsWithParent:!0,excludeEnd:!0,contains:a,keywords:e},t={begin:/\{/, +end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/, +contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(l,{begin:/:/ +})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(l)], +illegal:"\\S"};return a.push(t,s),i.forEach((n=>{a.push(n)})),{name:"JSON", +contains:a,keywords:e,illegal:"\\S"}}})()); +hljs.registerLanguage("kotlin",(()=>{"use strict" +;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ +className:"number",variants:[{ +begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` +},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ +begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` +},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};return e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},i={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"meta-string"})]}] +},o=a,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},i,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin:/</,end:/>/, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 +},e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{className:"class", +beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin:/</,end:/>/,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/, +excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},o]}}})()); +hljs.registerLanguage("less",(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],n=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse(),r=i.concat(o) +;return a=>{const s=(e=>({IMPORTANT:{className:"meta",begin:"!important"}, +HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"}, +ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/, +illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]} +}))(a),l=r,d="([\\w-]+|@\\{[\\w-]+\\})",c=[],g=[],b=e=>({className:"string", +begin:"~?"+e+".*?"+e}),m=(e,t,i)=>({className:e,begin:t,relevance:i}),u={ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:t.join(" ")},p={ +begin:"\\(",end:"\\)",contains:g,keywords:u,relevance:0} +;g.push(a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,b("'"),b('"'),a.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},s.HEXCOLOR,p,m("variable","@@?[\\w-]+",10),m("variable","@\\{[\\w-]+\\}"),m("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},s.IMPORTANT);const f=g.concat({begin:/\{/,end:/\}/,contains:c}),h={ +beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not" +}].concat(g)},w={begin:d+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0, +contains:[{begin:/-(webkit|moz|ms|o)-/},{className:"attribute", +begin:"\\b("+n.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0, +illegal:"[<=$]",relevance:0,contains:g}}]},v={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:u,returnEnd:!0,contains:g,relevance:0}},y={ +className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{ +begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:f}},k={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:d,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,h,m("keyword","all\\b"),m("variable","@\\{[\\w-]+\\}"),{ +begin:"\\b("+e.join("|")+")\\b",className:"selector-tag" +},m("selector-tag",d+"%?",0),m("selector-id","#"+d),m("selector-class","\\."+d,0),m("selector-tag","&",0),s.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+i.join("|")+")"},{ +className:"selector-pseudo",begin:"::("+o.join("|")+")"},{begin:"\\(",end:"\\)", +contains:f},{begin:"!important"}]},E={begin:`[\\w-]+:(:)?(${l.join("|")})`, +returnBegin:!0,contains:[k]} +;return c.push(a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,v,y,E,w,k),{ +name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:c}}})()); +hljs.registerLanguage("lua",(()=>{"use strict";return e=>{ +const t="\\[=*\\[",a="\\]=*\\]",n={begin:t,end:a,contains:["self"] +},o=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",a,{contains:[n], +relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:o}].concat(o) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:t,end:a,contains:[n],relevance:5}])}}})()); +hljs.registerLanguage("makefile",(()=>{"use strict";return e=>{const i={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%<?\^\+\*]/}]},a={className:"string", +begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,i]},n={className:"variable", +begin:/\$\([\w-]+\s/,end:/\)/,keywords:{ +built_in:"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value" +},contains:[i]},s={begin:"^"+e.UNDERSCORE_IDENT_RE+"\\s*(?=[:+?]?=)"},r={ +className:"section",begin:/^[^\s]+:/,end:/$/,contains:[i]};return{ +name:"Makefile",aliases:["mk","mak","make"],keywords:{$pattern:/[\w-]+/, +keyword:"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath" +},contains:[e.HASH_COMMENT_MODE,i,a,n,s,{className:"meta",begin:/^\.PHONY:/, +end:/$/,keywords:{$pattern:/[\.\w]+/,"meta-keyword":".PHONY"}},r]}}})()); +hljs.registerLanguage("xml",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")} +function a(...n){return n.map((n=>e(n))).join("")}function s(...n){ +return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ +const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),i={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/, +contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},c=e.inherit(r,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{ +className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{ +className:"meta-string"}),m={endsWithParent:!0,illegal:/</,relevance:0, +contains:[{className:"attr",begin:/[A-Za-z0-9._:-]+/,relevance:0},{begin:/=\s*/, +relevance:0,contains:[{className:"string",endsParent:!0,variants:[{begin:/"/, +end:/"/,contains:[i]},{begin:/'/,end:/'/,contains:[i]},{begin:/[^\s"'=<>`]+/}]}] +}]};return{name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,contains:[{className:"meta",begin:/<![a-z]/,end:/>/, +relevance:10,contains:[r,g,l,c,{begin:/\[/,end:/\]/,contains:[{className:"meta", +begin:/<![a-z]/,end:/>/,contains:[r,c,g,l]}]}]},e.COMMENT(/<!--/,/-->/,{ +relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,relevance:10},i,{ +className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag", +begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:a(/</,n(a(t,s(/\/>/,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name", +begin:t,relevance:0,starts:m}]},{className:"tag",begin:a(/<\//,n(a(t,/>/))), +contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0, +endsParent:!0}]}]}}})()); +hljs.registerLanguage("markdown",(()=>{"use strict";function n(...n){ +return n.map((n=>{return(e=n)?"string"==typeof e?e:e.source:null;var e +})).join("")}return e=>{const a={begin:/<\/?[A-Za-z_]/,end:">", +subLanguage:"xml",relevance:0},i={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0 +},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{begin:n(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.+?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{ +className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},s={className:"strong",contains:[], +variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},c={ +className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{ +begin:/_(?!_)/,end:/_/,relevance:0}]};s.contains.push(c),c.contains.push(s) +;let t=[a,i] +;return s.contains=s.contains.concat(t),c.contains=c.contains.concat(t), +t=t.concat(s,c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:t},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:t}]}]},a,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},s,c,{className:"quote",begin:"^>\\s+",contains:t, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},i,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})()); +hljs.registerLanguage("nginx",(()=>{"use strict";return e=>{const n={ +className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/\}/},{ +begin:/[$@]/+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{ +$pattern:"[a-z/_]+", +literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll" +},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string", +contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/ +}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n] +},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^", +end:"\\s|\\{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|\\{|;",returnEnd:!0},{ +begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number", +begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{ +className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{ +name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{ +begin:e.UNDERSCORE_IDENT_RE+"\\s+\\{",returnBegin:!0,end:/\{/,contains:[{ +className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{ +begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|\\{",returnBegin:!0,contains:[{ +className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}], +illegal:"[^\\s\\}]"}}})()); +hljs.registerLanguage("objectivec",(()=>{"use strict";return e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n, +keyword:"@interface @class @protocol @implementation"};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{$pattern:n, +keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN", +literal:"false true FALSE TRUE nil YES NO NULL", +built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once" +},illegal:"</",contains:[{className:"built_in", +begin:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.C_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{ +className:"string",variants:[{begin:'@"',end:'"',illegal:"\\n", +contains:[e.BACKSLASH_ESCAPE]}]},{className:"meta",begin:/#\s*[a-z]+\b/,end:/$/, +keywords:{ +"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(e.QUOTE_STRING_MODE,{ +className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/, +illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:/(\{|$)/, +excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{ +begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}})()); +hljs.registerLanguage("perl",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(...n){ +return n.map((n=>e(n))).join("")}function t(...n){ +return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ +const r=/[dualxmsipngr]{0,12}/,s={$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},a={begin:/->\{/, +end:/\}/},o={variants:[{begin:/\$\d/},{ +begin:n(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},c=[e.BACKSLASH_ESCAPE,i,o],g=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],l=(e,t,s="\\1")=>{ +const i="\\1"===s?s:n(s,t) +;return n(n("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,i,/(?:\\.|[^\\\/])*?/,s,r) +},d=(e,t,s)=>n(n("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,s,r),p=[o,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),a,{className:"string",contains:c,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:l("s|tr|y",t(...g))},{begin:l("s|tr|y","\\(","\\)")},{ +begin:l("s|tr|y","\\[","\\]")},{begin:l("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",t(...g),/\1/)},{ +begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=p,a.contains=p,{name:"Perl",aliases:["pl","pm"],keywords:s, +contains:p}}})()); +hljs.registerLanguage("php",(()=>{"use strict";return e=>{const r={ +className:"variable", +begin:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?![A-Za-z0-9])(?![$])"},t={ +className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{ +begin:/\?>/}]},a={className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/, +end:/\}/}]},n=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),i=e.inherit(e.QUOTE_STRING_MODE,{illegal:null, +contains:e.QUOTE_STRING_MODE.contains.concat(a)}),o=e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(a)}),l={className:"string", +contains:[e.BACKSLASH_ESCAPE,t],variants:[e.inherit(n,{begin:"b'",end:"'" +}),e.inherit(i,{begin:'b"',end:'"'}),i,n,o]},s={className:"number",variants:[{ +begin:"\\b0b[01]+(?:_[01]+)*\\b"},{begin:"\\b0o[0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0x[\\da-f]+(?:_[\\da-f]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:e[+-]?\\d+)?" +}],relevance:0},c={ +keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile enum eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list match|0 mixed new object or private protected public real return string switch throw trait try unset use var void while xor yield", +literal:"false null true", +built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException UnhandledMatchError ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Stringable Throwable Traversable WeakReference WeakMap Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass" +};return{aliases:["php3","php4","php5","php6","php7","php8"], +case_insensitive:!0,keywords:c, +contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t] +}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}] +}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0, +keywords:"__halt_compiler"}),t,{className:"keyword",begin:/\$this\b/},r,{ +begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function", +relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0, +illegal:"[$%\\[]",contains:[{beginKeywords:"use"},e.UNDERSCORE_TITLE_MODE,{ +begin:"=>",endsParent:!0},{className:"params",begin:"\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0,keywords:c, +contains:["self",r,e.C_BLOCK_COMMENT_MODE,l,s]}]},{className:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",relevance:0,end:";", +contains:[e.UNDERSCORE_TITLE_MODE]},l,s]}}})()); +hljs.registerLanguage("php-template",(()=>{"use strict";return n=>({ +name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/, +subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"', +end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{ +illegal:null,className:null,contains:null,skip:!0 +}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0})]}]})})()); +hljs.registerLanguage("plaintext",(()=>{"use strict";return t=>({ +name:"Plain text",aliases:["text","txt"],disableAutodetect:!0})})()); +hljs.registerLanguage("properties",(()=>{"use strict";return e=>{ +var n="[ \\t\\f]*",a=n+"[:=]"+n,t="("+a+"|[ \\t\\f]+)",r="([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",s="([^\\\\:= \\t\\f\\n]|\\\\.)+",i={ +end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{ +begin:"\\\\\\\\"},{begin:"\\\\\\n"}]}};return{name:".properties", +case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{ +returnBegin:!0,variants:[{begin:r+a,relevance:1},{begin:r+"[ \\t\\f]+", +relevance:0}],contains:[{className:"attr",begin:r,endsParent:!0,relevance:0}], +starts:i},{begin:s+t,returnBegin:!0,relevance:0,contains:[{className:"meta", +begin:s,endsParent:!0,relevance:0}],starts:i},{className:"attr",relevance:0, +begin:s+n+"$"}]}}})()); +hljs.registerLanguage("python",(()=>{"use strict";return e=>{const n={ +$pattern:/[A-Za-z]\w+|__\w+__/, +keyword:["and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"], +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},a={className:"meta",begin:/^(>>>|\.\.\.) /},i={className:"subst",begin:/\{/, +end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},t={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},r="[0-9](_?[0-9])*",l=`(\\b(${r}))?\\.(${r})|\\b(${r})\\.`,b={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${r})|(${l}))[eE][+-]?(${r})[jJ]?\\b`},{begin:`(${l})[jJ]?`},{ +begin:"\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b"},{ +begin:"\\b0[bB](_?[01])+[lL]?\\b"},{begin:"\\b0[oO](_?[0-7])+[lL]?\\b"},{ +begin:"\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b"},{begin:`\\b(${r})[jJ]\\b`}]},o={ +className:"comment", +begin:(d=/# type:/,((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?=",d,")")), +end:/$/,keywords:n,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/, +endsWithParent:!0}]},c={className:"params",variants:[{className:"", +begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0, +keywords:n,contains:["self",a,b,t,e.HASH_COMMENT_MODE]}]};var d +;return i.contains=[t,b,a],{name:"Python",aliases:["py","gyp","ipython"], +keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,b,{begin:/\bself\b/},{ +beginKeywords:"if",relevance:0},t,o,e.HASH_COMMENT_MODE,{variants:[{ +className:"function",beginKeywords:"def"},{className:"class", +beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/, +contains:[e.UNDERSCORE_TITLE_MODE,c,{begin:/->/,endsWithParent:!0,keywords:n}] +},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[b,c,t]}]}}})()); +hljs.registerLanguage("python-repl",(()=>{"use strict";return s=>({ +aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$", +subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{ +begin:/^\.\.\.(?=[ ]|$)/}]}]})})()); +hljs.registerLanguage("r",(()=>{"use strict";function e(...e){return e.map((e=>{ +return(a=e)?"string"==typeof a?a:a.source:null;var a})).join("")}return a=>{ +const n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/;return{name:"R", +illegal:/->/,keywords:{$pattern:n, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},compilerExtensions:[(a,n)=>{if(!a.beforeMatch)return +;if(a.starts)throw Error("beforeMatch cannot be used with starts") +;const i=Object.assign({},a);Object.keys(a).forEach((e=>{delete a[e] +})),a.begin=e(i.beforeMatch,e("(?=",i.begin,")")),a.starts={relevance:0, +contains:[Object.assign(i,{endsParent:!0})]},a.relevance=0,delete i.beforeMatch +}],contains:[a.COMMENT(/#'/,/$/,{contains:[{className:"doctag", +begin:"@examples",starts:{contains:[{begin:/\n/},{begin:/#'\s*(?=@[a-zA-Z]+)/, +endsParent:!0},{begin:/#'/,end:/$/,excludeBegin:!0}]}},{className:"doctag", +begin:"@param",end:/$/,contains:[{className:"variable",variants:[{begin:n},{ +begin:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{className:"doctag", +begin:/@[a-zA-Z]+/},{className:"meta-keyword",begin:/\\[a-zA-Z]+/}] +}),a.HASH_COMMENT_MODE,{className:"string",contains:[a.BACKSLASH_ESCAPE], +variants:[a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{className:"number",relevance:0, +beforeMatch:/([^a-zA-Z0-9._])/,variants:[{ +match:/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/},{ +match:/0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/},{ +match:/(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/}]},{begin:"%",end:"%"},{ +begin:e(/[a-zA-Z][a-zA-Z_0-9]*/,"\\s+<-\\s+")},{begin:"`",end:"`",contains:[{ +begin:/\\./}]}]}}})()); +hljs.registerLanguage("ruby",(()=>{"use strict";function e(...e){ +return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n +})).join("")}return n=>{ +const a="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",i={ +keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__", +built_in:"proc lambda",literal:"true false nil"},s={className:"doctag", +begin:"@[A-Za-z]+"},r={begin:"#<",end:">"},b=[n.COMMENT("#","$",{contains:[s] +}),n.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),n.COMMENT("^__END__","\\n$")],c={className:"subst",begin:/#\{/,end:/\}/, +keywords:i},t={className:"string",contains:[n.BACKSLASH_ESCAPE,c],variants:[{ +begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/, +end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{ +begin:/%[qQwWx]?</,end:/>/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/, +end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{ +begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{ +begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:/<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,returnBegin:!0,contains:[{ +begin:/<<[-~]?'?/},n.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[n.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",d={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},l={className:"params",begin:"\\(",end:"\\)", +endsParent:!0,keywords:i},o=[t,{className:"class",beginKeywords:"class module", +end:"$|;",illegal:/=/,contains:[n.inherit(n.TITLE_MODE,{ +begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{ +begin:"("+n.IDENT_RE+"::)?"+n.IDENT_RE,relevance:0}]}].concat(b)},{ +className:"function",begin:e(/def\s+/,(_=a+"\\s*(\\(|;|$)",e("(?=",_,")"))), +relevance:0,keywords:"def",end:"$|;",contains:[n.inherit(n.TITLE_MODE,{begin:a +}),l].concat(b)},{begin:n.IDENT_RE+"::"},{className:"symbol", +begin:n.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[t,{begin:a}],relevance:0},d,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:i},{ +begin:"("+n.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{ +className:"regexp",contains:[n.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{ +begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(", +end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}] +}].concat(r,b),relevance:0}].concat(r,b);var _;c.contains=o,l.contains=o +;const E=[{begin:/^\s*=>/,starts:{end:"$",contains:o}},{className:"meta", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",contains:o}}];return b.unshift(r),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:i,illegal:/\/\*/, +contains:[n.SHEBANG({binary:"ruby"})].concat(E).concat(b).concat(o)}}})()); +hljs.registerLanguage("rust",(()=>{"use strict";return e=>{ +const n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!" +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?", +keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield", +literal:"true false Some None Ok Err",built_in:t},illegal:"</", +contains:[e.C_LINE_COMMENT_MODE,e.COMMENT("/\\*","\\*/",{contains:["self"] +}),e.inherit(e.QUOTE_STRING_MODE,{begin:/b?"/,illegal:null}),{ +className:"string",variants:[{begin:/r(#*)"(.|\n)*?"\1(?!#)/},{ +begin:/b?'\\?(x\w{2}|u\w{4}|U\w{8}|.)'/}]},{className:"symbol", +begin:/'[a-zA-Z_][a-zA-Z0-9_]*/},{className:"number",variants:[{ +begin:"\\b0b([01_]+)"+n},{begin:"\\b0o([0-7_]+)"+n},{ +begin:"\\b0x([A-Fa-f0-9_]+)"+n},{ +begin:"\\b(\\d[\\d_]*(\\.[0-9_]+)?([eE][+-]?[0-9_]+)?)"+n}],relevance:0},{ +className:"function",beginKeywords:"fn",end:"(\\(|<)",excludeEnd:!0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"meta",begin:"#!?\\[",end:"\\]", +contains:[{className:"meta-string",begin:/"/,end:/"/}]},{className:"class", +beginKeywords:"type",end:";",contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{ +endsParent:!0})],illegal:"\\S"},{className:"class", +beginKeywords:"trait enum struct union",end:/\{/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{endsParent:!0})],illegal:"[\\w\\d]" +},{begin:e.IDENT_RE+"::",keywords:{built_in:t}},{begin:"->"}]}}})()); +hljs.registerLanguage("scss",(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],r=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse() +;return a=>{const n=(e=>({IMPORTANT:{className:"meta",begin:"!important"}, +HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"}, +ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/, +illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]} +}))(a),l=o,s=i,d="@[a-z-]+",c={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"};return{name:"SCSS",case_insensitive:!0, +illegal:"[=/|']",contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+e.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+s.join("|")+")"},{className:"selector-pseudo", +begin:"::("+l.join("|")+")"},c,{begin:/\(/,end:/\)/,contains:[a.CSS_NUMBER_MODE] +},{className:"attribute",begin:"\\b("+r.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:":",end:";", +contains:[c,n.HEXCOLOR,a.CSS_NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.IMPORTANT] +},{begin:"@(page|font-face)",lexemes:d,keywords:"@page @font-face"},{begin:"@", +end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:t.join(" ")},contains:[{begin:d, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},c,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.HEXCOLOR,a.CSS_NUMBER_MODE]}]}} +})()); +hljs.registerLanguage("shell",(()=>{"use strict";return s=>({ +name:"Shell Session",aliases:["console"],contains:[{className:"meta", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#]/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]})})()); +hljs.registerLanguage("sql",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function r(...r){ +return r.map((r=>e(r))).join("")}function t(...r){ +return"("+r.map((r=>e(r))).join("|")+")"}return e=>{ +const n=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],s=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],o=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],c=s,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update ","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!s.includes(e))),u={ +begin:r(/\b/,t(...c),/\s*\(/),keywords:{built_in:c}};return{name:"SQL", +case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/, +keyword:((e,{exceptions:r,when:t}={})=>{const n=t +;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:t(...o),keywords:{$pattern:/[\w\.]+/,keyword:l.concat(o), +literal:a,type:i}},{className:"type", +begin:t("double precision","large object","with timezone","without timezone") +},u,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{ +begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{ +begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,n,{className:"operator", +begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}})()); +hljs.registerLanguage("swift",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")} +function a(...n){return n.map((n=>e(n))).join("")}function t(...n){ +return"("+n.map((n=>e(n))).join("|")+")"} +const i=e=>a(/\b/,e,/\w$/.test(e)?/\b/:/\B/),s=["Protocol","Type"].map(i),u=["init","self"].map(i),c=["Any","Self"],r=["associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],o=["false","nil","true"],l=["assignment","associativity","higherThan","left","lowerThan","none","right"],m=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],d=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],p=t(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=t(p,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),b=a(p,F,"*"),h=t(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),f=t(h,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),w=a(h,f,"*"),y=a(/[A-Z]/,f,"*"),g=["autoclosure",a(/convention\(/,t("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",a(/objc\(/,w,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","testable","UIApplicationMain","unknown","usableFromInline"],E=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;return e=>{const p={match:/\s+/,relevance:0},h=e.COMMENT("/\\*","\\*/",{ +contains:["self"]}),v=[e.C_LINE_COMMENT_MODE,h],N={className:"keyword", +begin:a(/\./,n(t(...s,...u))),end:t(...s,...u),excludeBegin:!0},A={ +match:a(/\./,t(...r)),relevance:0 +},C=r.filter((e=>"string"==typeof e)).concat(["_|0"]),_={variants:[{ +className:"keyword", +match:t(...r.filter((e=>"string"!=typeof e)).concat(c).map(i),...u)}]},D={ +$pattern:t(/\b\w+/,/#\w+/),keyword:C.concat(m),literal:o},B=[N,A,_],k=[{ +match:a(/\./,t(...d)),relevance:0},{className:"built_in", +match:a(/\b/,t(...d),/(?=\()/)}],M={match:/->/,relevance:0},S=[M,{ +className:"operator",relevance:0,variants:[{match:b},{match:`\\.(\\.|${F})+`}] +}],x="([0-9a-fA-F]_*)+",I={className:"number",relevance:0,variants:[{ +match:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{ +match:`\\b0x(${x})(\\.(${x}))?([pP][+-]?(([0-9]_*)+))?\\b`},{ +match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},O=(e="")=>({ +className:"subst",variants:[{match:a(/\\/,e,/[0\\tnr"']/)},{ +match:a(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),T=(e="")=>({className:"subst", +match:a(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),L=(e="")=>({className:"subst", +label:"interpol",begin:a(/\\/,e,/\(/),end:/\)/}),P=(e="")=>({begin:a(e,/"""/), +end:a(/"""/,e),contains:[O(e),T(e),L(e)]}),$=(e="")=>({begin:a(e,/"/), +end:a(/"/,e),contains:[O(e),L(e)]}),K={className:"string", +variants:[P(),P("#"),P("##"),P("###"),$(),$("#"),$("##"),$("###")]},j={ +match:a(/`/,w,/`/)},z=[j,{className:"variable",match:/\$\d+/},{ +className:"variable",match:`\\$${f}+`}],q=[{match:/(@|#)available/, +className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:E, +contains:[...S,I,K]}]}},{className:"keyword",match:a(/@/,t(...g))},{ +className:"meta",match:a(/@/,w)}],U={match:n(/\b[A-Z]/),relevance:0,contains:[{ +className:"type", +match:a(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+") +},{className:"type",match:y,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:a(/\s+&\s+/,n(y)),relevance:0}]},Z={ +begin:/</,end:/>/,keywords:D,contains:[...v,...B,...q,M,U]};U.contains.push(Z) +;const G={begin:/\(/,end:/\)/,relevance:0,keywords:D,contains:["self",{ +match:a(w,/\s*:/),keywords:"_|0",relevance:0 +},...v,...B,...k,...S,I,K,...z,...q,U]},H={beginKeywords:"func",contains:[{ +className:"title",match:t(j.match,w,b),endsParent:!0,relevance:0},p]},R={ +begin:/</,end:/>/,contains:[...v,U]},V={begin:/\(/,end:/\)/,keywords:D, +contains:[{begin:t(n(a(w,/\s*:/)),n(a(w,/\s+/,w,/\s*:/))),end:/:/,relevance:0, +contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:w}] +},...v,...B,...S,I,K,...q,U,G],endsParent:!0,illegal:/["']/},W={ +className:"function",match:n(/\bfunc\b/),contains:[H,R,V,p],illegal:[/\[/,/%/] +},X={className:"function",match:/\b(subscript|init[?!]?)\s*(?=[<(])/,keywords:{ +keyword:"subscript init init? init!",$pattern:/\w+[?!]?/},contains:[R,V,p], +illegal:/\[|%/},J={beginKeywords:"operator",end:e.MATCH_NOTHING_RE,contains:[{ +className:"title",match:b,endsParent:!0,relevance:0}]},Q={ +beginKeywords:"precedencegroup",end:e.MATCH_NOTHING_RE,contains:[{ +className:"title",match:y,relevance:0},{begin:/{/,end:/}/,relevance:0, +endsParent:!0,keywords:[...l,...o],contains:[U]}]};for(const e of K.variants){ +const n=e.contains.find((e=>"interpol"===e.label));n.keywords=D +;const a=[...B,...k,...S,I,K,...z];n.contains=[...a,{begin:/\(/,end:/\)/, +contains:["self",...a]}]}return{name:"Swift",keywords:D,contains:[...v,W,X,{ +className:"class",beginKeywords:"struct protocol class extension enum", +end:"\\{",excludeEnd:!0,keywords:D,contains:[e.inherit(e.TITLE_MODE,{ +begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...B]},J,Q,{ +beginKeywords:"import",end:/$/,contains:[...v],relevance:0 +},...B,...k,...S,I,K,...z,...q,U,G]}}})()); +hljs.registerLanguage("typescript",(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) +;function t(e){return r("(?=",e,")")}function r(...e){return e.map((e=>{ +return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return i=>{ +const c={$pattern:e, +keyword:n.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]), +literal:a, +built_in:s.concat(["any","void","number","boolean","string","object","never","enum"]) +},o={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},l=(e,n,a)=>{ +const s=e.contains.findIndex((e=>e.label===n)) +;if(-1===s)throw Error("can not find mode to replace");e.contains.splice(s,1,a) +},b=(i=>{const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,s=e.input[a];"<"!==s?">"===s&&(((e,{after:n})=>{ +const a="</"+e[0].slice(1);return-1!==e.input.indexOf(a,n)})(e,{after:a +})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n,literal:a, +built_in:s},b="\\.([0-9](_?[0-9])*)",d="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",g={ +className:"number",variants:[{ +begin:`(\\b(${d})((${b})|\\.)?|(${b}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{ +begin:`\\b(${d})\\b((${b})\\b|\\.)?|(${b})\\b`},{ +begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{ +begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{ +begin:"\\b0[0-7]+n?\\b"}],relevance:0},u={className:"subst",begin:"\\$\\{", +end:"\\}",keywords:l,contains:[]},E={begin:"html`",end:"",starts:{end:"`", +returnEnd:!1,contains:[i.BACKSLASH_ESCAPE,u],subLanguage:"xml"}},m={ +begin:"css`",end:"",starts:{end:"`",returnEnd:!1, +contains:[i.BACKSLASH_ESCAPE,u],subLanguage:"css"}},y={className:"string", +begin:"`",end:"`",contains:[i.BACKSLASH_ESCAPE,u]},_={className:"comment", +variants:[i.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{ +className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{", +end:"\\}",relevance:0},{className:"variable",begin:c+"(?=\\s*(-)|$)", +endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}] +}),i.C_BLOCK_COMMENT_MODE,i.C_LINE_COMMENT_MODE] +},p=[i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,E,m,y,g,i.REGEXP_MODE] +;u.contains=p.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(p) +});const N=[].concat(_,u.contains),f=N.concat([{begin:/\(/,end:/\)/,keywords:l, +contains:["self"].concat(N)}]),A={className:"params",begin:/\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f};return{name:"Javascript", +aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:f}, +illegal:/#(?![$_A-z])/,contains:[i.SHEBANG({label:"shebang",binary:"node", +relevance:5}),{label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,E,m,y,_,g,{ +begin:r(/[{,\n]\s*/,t(r(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))), +relevance:0,contains:[{className:"attr",begin:c+t("\\s*:"),relevance:0}]},{ +begin:"("+i.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",contains:[_,i.REGEXP_MODE,{className:"function", +begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+i.UNDERSCORE_IDENT_RE+")\\s*=>", +returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ +begin:i.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 +},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f}]}] +},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ +variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, +end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, +contains:["self"]}]}],relevance:0},{className:"function", +beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, +contains:["self",i.inherit(i.TITLE_MODE,{begin:c}),A],illegal:/%/},{ +beginKeywords:"while if switch catch for"},{className:"function", +begin:i.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,contains:[A,i.inherit(i.TITLE_MODE,{begin:c})]},{variants:[{ +begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", +beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ +beginKeywords:"extends"},i.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, +end:/[{;]/,excludeEnd:!0,contains:[i.inherit(i.TITLE_MODE,{begin:c}),"self",A] +},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", +contains:[i.inherit(i.TITLE_MODE,{begin:c}),{begin:/\(\)/},A]},{begin:/\$[(.]/}] +}})(i) +;return Object.assign(b.keywords,c),b.exports.PARAMS_CONTAINS.push(o),b.contains=b.contains.concat([o,{ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0},{beginKeywords:"interface", +end:/\{/,excludeEnd:!0,keywords:"interface extends" +}]),l(b,"shebang",i.SHEBANG()),l(b,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),b.contains.find((e=>"function"===e.className)).relevance=0,Object.assign(b,{ +name:"TypeScript",aliases:["ts","tsx"]}),b}})()); +hljs.registerLanguage("vbnet",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(...n){ +return n.map((n=>e(n))).join("")}function t(...n){ +return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ +const a=/\d{1,2}\/\d{1,2}\/\d{4}/,i=/\d{4}-\d{1,2}-\d{1,2}/,s=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,o={ +className:"literal",variants:[{begin:n(/# */,t(i,a),/ *#/)},{ +begin:n(/# */,r,/ *#/)},{begin:n(/# */,s,/ *#/)},{ +begin:n(/# */,t(i,a),/ +/,t(s,r),/ *#/)}]},l=e.COMMENT(/'''/,/$/,{contains:[{ +className:"doctag",begin:/<\/?/,end:/>/}]}),c=e.COMMENT(null,/$/,{variants:[{ +begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET", +aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},o,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},l,c,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +"meta-keyword":"const disable else elseif enable end externalsource if region then" +},contains:[c]}]}}})()); +hljs.registerLanguage("yaml",(()=>{"use strict";return e=>{ +var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, +end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", +contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", +begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] +;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:b}}})());
\ No newline at end of file diff --git a/www/js/site.js b/www/js/site.js new file mode 100644 index 0000000..66ce477 --- /dev/null +++ b/www/js/site.js @@ -0,0 +1 @@ +hljs.highlightAll(); diff --git a/www/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html b/www/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html index f2a2d21..7a8c0af 100644 --- a/www/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html +++ b/www/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Installing Arch Linux on a Thinkpad X220</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,7 +21,7 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal.</p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP.</p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk + <section><h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal. </p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP. </p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 535M 1 loop /run/archiso/sfs/airootfs @@ -40,7 +43,7 @@ sdb 8:16 1 7.4G 0 disk +-------------------+ +--------------------------------------------------------+</code></pre><p>The hard drive would be split into two partitions. The first, sdb1 would be 512MB in size and mounted in the file system at /boot. This would be the EFI system partition. The reminder of the disk space would be given to the partition sda2 and encrypted using LUKS2. LVM would then be used to create the volume group vg0 that would be divided into three partitions as logical volumes.</p><ul><li>/dev/vg0/root 50G root partition.</li><li>/dev/vg0/swap 16G swap partition.</li><li>/dev/vg0/home 200G home partition.</li></ul><p>Use fdisk to create the partitions.</p><pre><code class="shell">$ fdisk /dev/sda</code></pre><p>Enter g to create a new empty GPT partition table</p><pre><code class="shell">Command (m for help): g Created a new GPT disklabel (GUID: 6987D065-936E-1547-9F02-F78145025A96).</code></pre><p>Since this is a UEFI system there must be a EFI partition at the beginning of the disk. Enter n to add a new partition and enter 1 to assign it as the first partition. Use the default value for the first sector but enter +512M for the last sector.</p><pre><code class="shell">Command (m for help): n Partition number (1-128, default 1): 1 -First sector (2048-625142414, default 2048): +First sector (2048-625142414, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-625142414, default 625142414): +512M Created a new partition 1 of type 'Linux filesystem' and of size 512 MiB.</code></pre><p>Enter t to change the partition type and enter 1 to make it an EFI System. You can also get a list of partition types by pressing L.</p><pre><code class="shell">Command (m for help): t @@ -48,8 +51,8 @@ Selected partition 1 Partition type (type L to list all types): 1 Changed type of partition 'Linux filesystem' to 'EFI System'.</code></pre><p>To create the second partition enter n again to add another partition, and then enter 2 to assign it as the second partition. Use the default values for both first and last sectors to allocate the remainder of the drive.</p><pre><code class="shell">Command (m for help): n Partition number (2-128, default 2): 2 -First sector (1050624-625142414, default 1050624): -Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): +First sector (1050624-625142414, default 1050624): +Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): Created a new partition 2 of type 'Linux filesystem' and of size 297.6 GiB.</code></pre><p>Enter w to write the changes and quit.</p><pre><code class="shell">Command (m for help): w The partition table has been altered. @@ -59,40 +62,40 @@ Syncing disks.</code></pre><p>Use lsblk to confirm that two partitions have been NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 298.1G 0 disk ├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command.</p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition.</p><pre><code class="shell">WARNING! +└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command. </p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition. </p><pre><code class="shell">WARNING! ======== This will overwrite data on /dev/sda2 irrevocably. -Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: -Verify passphrase: -cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Verify passphrase: +cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: cryptsetup open /dev/sda2 cryptlvm 6.48s user 0.36s system 92% cpu 7.436 total</code></pre><p>Create a physical volume.</p><pre><code class="shell">$ pvcreate /dev/mapper/cryptlvm</code></pre><p>Create a volume group called vg0.</p><pre><code class="shell">$ vgcreate vg0 /dev/mapper/cryptlvm</code></pre><p>Create three logical volumes for the root, swap and home partitions.</p><pre><code class="shell">$ lvcreate -L 50G vg0 -n root $ lvcreate -L 16G vg0 -n swap $ lvcreate -L 200G vg0 -n home</code></pre><p>Make use of lsblk again to verify that LVM has been setup as expected.</p><pre><code class="shell">$ lsblk /dev/sda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT -sda 8:0 0 298.1G 0 disk -├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part -└─cryptlvm 254:0 0 297.6G 0 crypt -├─vg0-root 254:1 0 50G 0 lvm -├─vg0-swap 254:2 0 16G 0 lvm -└─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root +sda 8:0 0 298.1G 0 disk +├─sda1 8:1 0 512M 0 part +└─sda2 8:2 0 297.6G 0 part + └─cryptlvm 254:0 0 297.6G 0 crypt + ├─vg0-root 254:1 0 50G 0 lvm + ├─vg0-swap 254:2 0 16G 0 lvm + └─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root $ mkfs.ext4 /dev/vg0/home</code></pre><p>Initialise the swap partition.</p><pre><code class="shell">$ mkswap /dev/vg0/swap -$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot.</p><pre><code class="shell">$ mkdir /mnt/boot +$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot. </p><pre><code class="shell">$ mkdir /mnt/boot $ mount /dev/sda1 /mnt/boot</code></pre><p>Finally mount the home partition into /mnt/home.</p><pre><code class="shell">$ mkdir /mnt/home -$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting.</li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost +$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting. </li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost ::1 localhost 127.0.0.1 suliman.localdomain suliman</code></pre><h2>Wireless</h2><p>Use the ip command to determine the name of the wireless network interface.</p><pre><code class="shell">$ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 -link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 -link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff + link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff 3: wwp0s29u1u4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff + link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff 4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 + link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 Load module index Parsed configuration file /usr/lib/systemd/network/99-default.link @@ -104,16 +107,18 @@ ID_NET_NAME_MAC=wlx0811960210ac ID_OUI_FROM_DATABASE=Intel Corporate ID_NET_NAME_PATH=wlp3s0 Unload module index -Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line.</p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command.</p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead.</p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments.</p><pre>#!/bin/sh +Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line. </p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command. </p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead. </p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments. </p><pre>#!/bin/sh # Determine the UUID of the partition that is encrypted PARTUUID=`blkid /dev/sda2 -s PARTUUID -o value` efibootmgr \ ---disk /dev/sda --part 1 \ ---create --label "Arch Linux" \ ---loader /vmlinuz-linux \ ---unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ ---verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition.</p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject.</p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + --disk /dev/sda --part 1 \ + --create --label "Arch Linux" \ + --loader /vmlinuz-linux \ + --unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ + --verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition. </p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject. </p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html b/www/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html index c294c31..cfee88e 100644 --- a/www/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html +++ b/www/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Adding a User in Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -25,6 +28,8 @@ Retype new password: passwd: password updated successfully</code></pre><p>Now all I have to do is logout of the root account.</p><pre><code class="shell">$ logout</code></pre><p>Then login with the new credentials to confirm that everything is okay.</p><pre><code class="shell">suliman login: david Password: -david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html b/www/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html index 7072ec7..a18f4ed 100644 --- a/www/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html +++ b/www/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Granting Sudo Access to a User in Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,8 +21,10 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers.</p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed.</li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd + <section><h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers. </p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed. </li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd -/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html b/www/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html index b61891c..8c86987 100644 --- a/www/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html +++ b/www/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Installing ST, DMENU and DWM in Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -27,6 +30,8 @@ $ sudo make install</code></pre><h2>Configure and Install DMENU</h2><p>Again mov # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Again compiling and installing is done with the below commands.</p><pre><code class="shell">$ make clean $ sudo make install</code></pre><h2>Configure and Install DWM</h2><p>For the final time move to the directory created earlier.</p><pre><code class="shell">$ cd ~/.local/src/dwm</code></pre><p>As with dmenu the same edit needs to be made to the file config.mk.</p><pre><code class="shell">$ nvim config.mk</code></pre><pre><code class="vim"># XINERAMALIBS = -lXinerama # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Compile and install as usual.</p><pre><code class="shell">$ make clean -$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html b/www/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html index e11103e..e34f122 100644 --- a/www/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html +++ b/www/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Pacman Cheat Sheet For Ubuntu Users</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -40,6 +43,8 @@ $ pacman -Syu</code></pre><p>Since Arch uses a rolling release system there is n $ pacman -Scc</code></pre><p>Free up disk space by removing from the cache any packages that are no longer installed. Also removes any cached sync databases.</p><h2>Remove Used Dependencies</h2><pre><code class="shell">$ pacman --query --deps --unrequired --quiet | pacman --remove --recursive - -$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html b/www/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html index 413b436..601959b 100644 --- a/www/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html +++ b/www/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Enabling Audio in Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -22,6 +25,8 @@ $ amixer sset Speaker unmute $ amixer sset Headphone unmute</code></pre><p>You can test that the speakers are working with the speaker-test command.</p><pre><code class="shell">$ speaker-test -c 2</code></pre><p>If you are still getting no sound then it could be that the volume has been set to zero. Use amixer again to increase the volume.</p><pre><code class="shell">$ amixer sset Master 100% $ amixer sset Speaker 100% -$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.html b/www/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.html index f3640a2..0411eb1 100644 --- a/www/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.html +++ b/www/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Installing Zsh and Powerlevel10k on Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -29,6 +32,8 @@ $ zsh-newuser-install -f</code></pre><h2>Changing The Default Shell</h2><p>Insta /bin/zsh /usr/bin/zsh</code></pre><p>The same command can be used to change the default shell by providing it with the full path to the shell.</p><pre><code class="shell">$ chsh -s /bin/zsh</code></pre><p>Note that this change is not instant and you will need to log out and log in again for it to take affect. Once you have done that check the SHELL environment variable again to confirm the change.</p><pre><code class="shell">$ echo $SHELL -/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/arch/atom.xml b/www/posts/arch/atom.xml index 53408ef..74b15d1 100644 --- a/www/posts/arch/atom.xml +++ b/www/posts/arch/atom.xml @@ -23,7 +23,7 @@ $ zsh-newuser-install -f</code></pre><h2>Changing The Default Shell</h2><p>Insta /bin/zsh /usr/bin/zsh</code></pre><p>The same command can be used to change the default shell by providing it with the full path to the shell.</p><pre><code class="shell">$ chsh -s /bin/zsh</code></pre><p>Note that this change is not instant and you will need to log out and log in again for it to take affect. Once you have done that check the SHELL environment variable again to confirm the change.</p><pre><code class="shell">$ echo $SHELL -/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Enabling Audio in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html</id> @@ -35,7 +35,7 @@ $ zsh-newuser-install -f</code></pre><h2>Changing The Default Shell</h2><p>Insta $ amixer sset Speaker unmute $ amixer sset Headphone unmute</code></pre><p>You can test that the speakers are working with the speaker-test command.</p><pre><code class="shell">$ speaker-test -c 2</code></pre><p>If you are still getting no sound then it could be that the volume has been set to zero. Use amixer again to increase the volume.</p><pre><code class="shell">$ amixer sset Master 100% $ amixer sset Speaker 100% -$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Pacman Cheat Sheet For Ubuntu Users</title> <id>https://davidtsadler.com/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html</id> @@ -65,7 +65,7 @@ $ pacman -Syu</code></pre><p>Since Arch uses a rolling release system there is n $ pacman -Scc</code></pre><p>Free up disk space by removing from the cache any packages that are no longer installed. Also removes any cached sync databases.</p><h2>Remove Used Dependencies</h2><pre><code class="shell">$ pacman --query --deps --unrequired --quiet | pacman --remove --recursive - -$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing ST, DMENU and DWM in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html</id> @@ -82,7 +82,7 @@ $ sudo make install</code></pre><h2>Configure and Install DMENU</h2><p>Again mov # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Again compiling and installing is done with the below commands.</p><pre><code class="shell">$ make clean $ sudo make install</code></pre><h2>Configure and Install DWM</h2><p>For the final time move to the directory created earlier.</p><pre><code class="shell">$ cd ~/.local/src/dwm</code></pre><p>As with dmenu the same edit needs to be made to the file config.mk.</p><pre><code class="shell">$ nvim config.mk</code></pre><pre><code class="vim"># XINERAMALIBS = -lXinerama # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Compile and install as usual.</p><pre><code class="shell">$ make clean -$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Granting Sudo Access to a User in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html</id> @@ -90,9 +90,9 @@ $ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed x <author><name>David T. Sadler.</name></author> <published>2020-06-22T12:00:00Z</published> <updated>2020-06-22T12:00:00Z</updated> - <content>![CDATA[<h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers.</p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed.</li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd + <content>![CDATA[<h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers. </p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed. </li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd -/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Adding a User in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html</id> @@ -107,7 +107,7 @@ Retype new password: passwd: password updated successfully</code></pre><p>Now all I have to do is logout of the root account.</p><pre><code class="shell">$ logout</code></pre><p>Then login with the new credentials to confirm that everything is okay.</p><pre><code class="shell">suliman login: david Password: -david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing Arch Linux on a Thinkpad X220</title> <id>https://davidtsadler.com/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html</id> @@ -115,7 +115,7 @@ david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/instal <author><name>David T. Sadler.</name></author> <published>2020-05-25T12:00:00Z</published> <updated>2020-05-25T12:00:00Z</updated> - <content>![CDATA[<h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal.</p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP.</p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk + <content>![CDATA[<h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal. </p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP. </p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 535M 1 loop /run/archiso/sfs/airootfs @@ -137,7 +137,7 @@ sdb 8:16 1 7.4G 0 disk +-------------------+ +--------------------------------------------------------+</code></pre><p>The hard drive would be split into two partitions. The first, sdb1 would be 512MB in size and mounted in the file system at /boot. This would be the EFI system partition. The reminder of the disk space would be given to the partition sda2 and encrypted using LUKS2. LVM would then be used to create the volume group vg0 that would be divided into three partitions as logical volumes.</p><ul><li>/dev/vg0/root 50G root partition.</li><li>/dev/vg0/swap 16G swap partition.</li><li>/dev/vg0/home 200G home partition.</li></ul><p>Use fdisk to create the partitions.</p><pre><code class="shell">$ fdisk /dev/sda</code></pre><p>Enter g to create a new empty GPT partition table</p><pre><code class="shell">Command (m for help): g Created a new GPT disklabel (GUID: 6987D065-936E-1547-9F02-F78145025A96).</code></pre><p>Since this is a UEFI system there must be a EFI partition at the beginning of the disk. Enter n to add a new partition and enter 1 to assign it as the first partition. Use the default value for the first sector but enter +512M for the last sector.</p><pre><code class="shell">Command (m for help): n Partition number (1-128, default 1): 1 -First sector (2048-625142414, default 2048): +First sector (2048-625142414, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-625142414, default 625142414): +512M Created a new partition 1 of type 'Linux filesystem' and of size 512 MiB.</code></pre><p>Enter t to change the partition type and enter 1 to make it an EFI System. You can also get a list of partition types by pressing L.</p><pre><code class="shell">Command (m for help): t @@ -145,8 +145,8 @@ Selected partition 1 Partition type (type L to list all types): 1 Changed type of partition 'Linux filesystem' to 'EFI System'.</code></pre><p>To create the second partition enter n again to add another partition, and then enter 2 to assign it as the second partition. Use the default values for both first and last sectors to allocate the remainder of the drive.</p><pre><code class="shell">Command (m for help): n Partition number (2-128, default 2): 2 -First sector (1050624-625142414, default 1050624): -Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): +First sector (1050624-625142414, default 1050624): +Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): Created a new partition 2 of type 'Linux filesystem' and of size 297.6 GiB.</code></pre><p>Enter w to write the changes and quit.</p><pre><code class="shell">Command (m for help): w The partition table has been altered. @@ -156,40 +156,40 @@ Syncing disks.</code></pre><p>Use lsblk to confirm that two partitions have been NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 298.1G 0 disk ├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command.</p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition.</p><pre><code class="shell">WARNING! +└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command. </p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition. </p><pre><code class="shell">WARNING! ======== This will overwrite data on /dev/sda2 irrevocably. -Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: -Verify passphrase: -cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Verify passphrase: +cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: cryptsetup open /dev/sda2 cryptlvm 6.48s user 0.36s system 92% cpu 7.436 total</code></pre><p>Create a physical volume.</p><pre><code class="shell">$ pvcreate /dev/mapper/cryptlvm</code></pre><p>Create a volume group called vg0.</p><pre><code class="shell">$ vgcreate vg0 /dev/mapper/cryptlvm</code></pre><p>Create three logical volumes for the root, swap and home partitions.</p><pre><code class="shell">$ lvcreate -L 50G vg0 -n root $ lvcreate -L 16G vg0 -n swap $ lvcreate -L 200G vg0 -n home</code></pre><p>Make use of lsblk again to verify that LVM has been setup as expected.</p><pre><code class="shell">$ lsblk /dev/sda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT -sda 8:0 0 298.1G 0 disk -├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part -└─cryptlvm 254:0 0 297.6G 0 crypt -├─vg0-root 254:1 0 50G 0 lvm -├─vg0-swap 254:2 0 16G 0 lvm -└─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root +sda 8:0 0 298.1G 0 disk +├─sda1 8:1 0 512M 0 part +└─sda2 8:2 0 297.6G 0 part + └─cryptlvm 254:0 0 297.6G 0 crypt + ├─vg0-root 254:1 0 50G 0 lvm + ├─vg0-swap 254:2 0 16G 0 lvm + └─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root $ mkfs.ext4 /dev/vg0/home</code></pre><p>Initialise the swap partition.</p><pre><code class="shell">$ mkswap /dev/vg0/swap -$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot.</p><pre><code class="shell">$ mkdir /mnt/boot +$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot. </p><pre><code class="shell">$ mkdir /mnt/boot $ mount /dev/sda1 /mnt/boot</code></pre><p>Finally mount the home partition into /mnt/home.</p><pre><code class="shell">$ mkdir /mnt/home -$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting.</li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost +$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting. </li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost ::1 localhost 127.0.0.1 suliman.localdomain suliman</code></pre><h2>Wireless</h2><p>Use the ip command to determine the name of the wireless network interface.</p><pre><code class="shell">$ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 -link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 -link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff + link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff 3: wwp0s29u1u4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff + link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff 4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 + link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 Load module index Parsed configuration file /usr/lib/systemd/network/99-default.link @@ -201,16 +201,16 @@ ID_NET_NAME_MAC=wlx0811960210ac ID_OUI_FROM_DATABASE=Intel Corporate ID_NET_NAME_PATH=wlp3s0 Unload module index -Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line.</p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command.</p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead.</p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments.</p><pre>#!/bin/sh +Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line. </p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command. </p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead. </p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments. </p><pre>#!/bin/sh # Determine the UUID of the partition that is encrypted PARTUUID=`blkid /dev/sda2 -s PARTUUID -o value` efibootmgr \ ---disk /dev/sda --part 1 \ ---create --label "Arch Linux" \ ---loader /vmlinuz-linux \ ---unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ ---verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition.</p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject.</p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + --disk /dev/sda --part 1 \ + --create --label "Arch Linux" \ + --loader /vmlinuz-linux \ + --unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ + --verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition. </p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject. </p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/arch/index.html b/www/posts/arch/index.html index 7e093b2..dccb40c 100644 --- a/www/posts/arch/index.html +++ b/www/posts/arch/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Arch</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Arch</h1><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Arch</h1><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/atom.xml b/www/posts/atom.xml index ef94171..3349eed 100644 --- a/www/posts/atom.xml +++ b/www/posts/atom.xml @@ -12,7 +12,7 @@ <author><name>David T. Sadler.</name></author> <published>2021-02-15T12:00:00Z</published> <updated>2021-02-15T12:00:00Z</updated> - <content>![CDATA[<h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + <content>![CDATA[<h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><h3>Links</h3><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">How to Host Your Own Gemini Site in the Cloud</title> <id>https://davidtsadler.com/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.html</id> @@ -30,12 +30,12 @@ $ ufw enable</code></pre><h3>Harden SSH</h3><p>I edited the /etc/ssh/sshd_config PasswordAuthentication no</pre><p>Since I'd made changes to the configuration I needed to restart the SSH service.</p><pre><code class="shell">$ service sshd restart</code></pre><h3>Create non-root User</h3><p>Whenever I access a server I like to login as a non-root user that is able to run sudo on the system.</p><pre><code class="shell">$ adduser gemini $ usermod -aG sudo gemini</code></pre><p>As the SSH key is already on the server I can copy it to the non-root user account.</p><pre><code class="shell">$ rsync --archive --chown=gemini:gemini ~/.ssh /home/gemini</code></pre><p>On my local system I confirm that I can log in as the new user without a password.</p><pre><code class="shell">$ ssh gemini@davidtsadler.co.uk -i ~/.ssh/davidtsadler.co.uk/id_rsa</code></pre><p>I also confirm that I have sudo access.</p><pre><code class="shell">$ sudo ls</code></pre><h2>Installing a Gemini Site and Server</h2><h3>Directory structure</h3><p>I decided to go with a very simple directory structure. Each site will be a sub-directory in ~/sites that will be named after the domain name. Then each site will have the following sub-directories. The idea is that I may want to host more than one site in the future.</p><ul><li>bin This will contain the Gemini server binary.</li><li>certs TLS certificates for the site are kept here.</li><li>public This will contain the .gmi files of the site.</li><li>scripts Contains scripts used to start the Gemini server.</li></ul><p>I created the directory structure with the below command.</p><pre><code class="shell">$ mkdir -p ~/sites/davidtsadler.co.uk/{bin,certs,public,scripts}</code></pre><h3>Install certificates</h3><p>Sine the Gemini protocol encourages using a self-signed certificate I installed one with the openssl command.</p><pre><code class="shell">$ openssl req -x509 \ --newkey rsa:4096 \ --keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ --out ~/sites/davidtsadler.co.uk/certs/cert.pem \ --days 3650 \ --nodes \ --subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi + -newkey rsa:4096 \ + -keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ + -out ~/sites/davidtsadler.co.uk/certs/cert.pem \ + -days 3650 \ + -nodes \ + -subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi # Welcome Hello world! @@ -51,13 +51,13 @@ $ chmod u+x agate</code></pre><p>I wrote a very simple bash script to run agate #!/bin/bash /home/gemini/sites/davidtsadler.co.uk/bin/agate \ ---content /home/gemini/sites/davidtsadler.co.uk/public/ \ ---key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ ---cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ ---addr [::]:1965 \ ---addr 0.0.0.0:1965 \ ---hostname davidtsadler.co.uk \ ---lang en-GB + --content /home/gemini/sites/davidtsadler.co.uk/public/ \ + --key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ + --cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ + --addr [::]:1965 \ + --addr 0.0.0.0:1965 \ + --hostname davidtsadler.co.uk \ + --lang en-GB EOF $ chmod u+x ~/sites/davidtsadler.co.uk/scripts/start</code></pre><h3>Testing the Site</h3><p>At this point I have the Gemini server installed and a site available for testing.</p><p>I first started agate with the bash script.</p><pre><code class="shell">$ ~/sites/davidtsadler.co.uk/scripts/start @@ -77,7 +77,7 @@ WantedBy=default.target</pre><p>I then started this service and confirmed it was $ sudo systemctl status agate.service -Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing PHP 8 for Windows 10</title> <id>https://davidtsadler.com/posts/php/2021-01-18/installing-php-8-for-windows-10/index.html</id> @@ -85,11 +85,11 @@ Active: active (running)</pre><p>The final step was to have this service start w <author><name>David T. Sadler.</name></author> <published>2021-01-18T12:00:00Z</published> <updated>2021-01-18T12:00:00Z</updated> - <content>![CDATA[<h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system.</p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable.</p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v + <content>![CDATA[<h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system. </p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable. </p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v PHP 8.0.1 (cli) (built: Jan 5 2021 23:43:33) ( NTS Visual C++ 2019 x64 ) Copyright (c) The PHP Group -Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing Laravel Homestead in Arch Linux</title> <id>https://davidtsadler.com/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.html</id> @@ -103,22 +103,22 @@ $ git checkout release</code></pre><p>Create the Homestead.yaml file by using th $ echo "<?php phpinfo();" > ~/projects/testsite/public/index.php</code></pre><h3>Setting up SSH</h3><p>I like to use unique ssh keys for servers that I connect to and that includes any virtual machines running on my local machine. The ssh-keygen command generates a new key that I store in a directory separate from my other ones.</p><pre><code class="shell">$ mkdir ~/.ssh/homestead -$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site.</p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file.</p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead +$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site. </p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file. </p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ nvim Homestead.yaml</code></pre><p>First tell Vagrant that Virtualbox will be providing the virtual machine.</p><pre><code class="yaml">set provider: virtualbox</code></pre><p>Vagrant needs to setup the ssh keys between the host and the guest so that you can connect via ssh. Enter the path to the one created earlier.</p><pre><code class="yaml">authorize: ~/.ssh/homestead/id_rsa.pub keys: -- ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: -- map: ~/projects/testsite -to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: -- map: testsite.local -to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: -- testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: -- mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead + - ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: + - map: ~/projects/testsite + to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: + - map: testsite.local + to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: + - testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: + - mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ vagrant up</code></pre><p>Once the machine is booted I can browse to http://testsite.local/ to see the simple site that is now served by Homestead.</p><h2>Installing a Laravel Site</h2><p>Now that Homestead is installed and serving a simple site its time to move onto installing the first Laravel application. Since Homestead provides all the tools required to do this the first thing to do is connect to the virtual machine.</p><pre><code class="shell">$ cd ~/.local/share/homestead -$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell ">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +--------------------+ | Database | @@ -148,8 +148,8 @@ DB_PORT=3306 DB_DATABASE=testsite DB_USERNAME=testsite DB_PASSWORD=testsite</pre><p>Now when you browse to http://testsite.local you will see the Laravel welcome page.</p><h2>Simplified SSH</h2><p>I prefer to just use the host system's ssh command to connect to Homestead as it cuts out having to navigate to the Homestead directory and running vagrant ssh.</p><p>To simplify ssh I first add a hostname for the virtual machine to the file /etc/hosts/</p><pre><code class="shell">192.168.10.10 homestead</code></pre><p>I then edit ~/.ssh/config and add the below configuration. This tells ssh to automatically use the keys and username specified when connecting to the virtual machine.</p><pre><code class="ssh">Host homestead -IdentityFile ~/.ssh/homestead/id_rsa -User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + IdentityFile ~/.ssh/homestead/id_rsa + User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</title> <id>https://davidtsadler.com/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html</id> @@ -161,22 +161,22 @@ User vagrant</code></pre><p>From now on I can simply do ssh homestead from any d Illuminate\Database\QueryException -SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') - -at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 -674▕ // If an exception occurs when attempting to run a query, we'll format the error -675▕ // message to include the bindings with SQL, which will make this exception a -676▕ // lot more helpful to the developer instead of just the database's errors. -677▕ catch (Exception $e) { -678▕ throw new QueryException( -679▕ $query, $this->prepareBindings($bindings), $e -680▕ ); -681▕ } -682▕ - -+33 vendor frames -34 artisan:37 -Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') + + at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 + 674▕ // If an exception occurs when attempting to run a query, we'll format the error + 675▕ // message to include the bindings with SQL, which will make this exception a + 676▕ // lot more helpful to the developer instead of just the database's errors. + 677▕ catch (Exception $e) { + 678▕ throw new QueryException( + 679▕ $query, $this->prepareBindings($bindings), $e + 680▕ ); + 681▕ } + 682▕ + + +33 vendor frames + 34 artisan:37 + Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing Zsh and Powerlevel10k on Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/index.html</id> @@ -195,7 +195,7 @@ $ zsh-newuser-install -f</code></pre><h2>Changing The Default Shell</h2><p>Insta /bin/zsh /usr/bin/zsh</code></pre><p>The same command can be used to change the default shell by providing it with the full path to the shell.</p><pre><code class="shell">$ chsh -s /bin/zsh</code></pre><p>Note that this change is not instant and you will need to log out and log in again for it to take affect. Once you have done that check the SHELL environment variable again to confirm the change.</p><pre><code class="shell">$ echo $SHELL -/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +/bin/ksh</code></pre><h2>Installing Powerlevel10k</h2><p>Powerlevel10k is a theme for Zsh and in order to make the most of it you should install the Meslo Nerd Font that has been patched for Powerlevel10k.</p><pre><code class="shell">$ yay -Sy --noconfirm ttf-meslo-nerd-font-powerlevel10k</code></pre><p>You need to configure your terminal to use this font. How this is done is dependant upon what terminal you are using. Since I use st from suckless I need to edit the file config.h and specify MesloLGS NF in the font settings.</p><pre><code class="shell">static char *font = "MesloLGS NF:pixelsize=14:antialias=true:autohint=true";</code></pre><p>Before rebuilding st.</p><pre><code class="shell">$ sudo make install</code></pre><p>Now that the dependences have been meet Powerlevel10k can be installed.</p><pre><code class="shell">$ yay -Sy --noconfirm zsh-theme-powerlevel10k-git</code></pre><p>Once it has been installed ensure that Zsh loads Powerlevel10k.</p><pre><code class="shell">$ echo 'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme' >>! ~/.zshrc</code></pre><p>You can now open a new terminal which will start the Powerlevel10k configuration wizard. This will ask you a few questions and configure your prompt. If it doesn't trigger automatically, type p10k configure.</p><p>Once the configuration wizard has finished open a new terminal and you should see the Powerlevel10k theme been used.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://www.zsh.org/">Zsh.</a><a href="https://github.com/romkatv/powerlevel10k/">Powerlevel10k.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Enabling Audio in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-08-31/enabling-audio-in-arch-linux/index.html</id> @@ -207,7 +207,7 @@ $ zsh-newuser-install -f</code></pre><h2>Changing The Default Shell</h2><p>Insta $ amixer sset Speaker unmute $ amixer sset Headphone unmute</code></pre><p>You can test that the speakers are working with the speaker-test command.</p><pre><code class="shell">$ speaker-test -c 2</code></pre><p>If you are still getting no sound then it could be that the volume has been set to zero. Use amixer again to increase the volume.</p><pre><code class="shell">$ amixer sset Master 100% $ amixer sset Speaker 100% -$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ amixer sset Headphone 100%</code></pre><h2>Unmute with Alsamixer</h2><p>If you prefer a more intuitive ncurses interface you can use alsamixer.</p><pre><code class="shell">$ alsamixer</code></pre><p>Channels that are muted will have the MM label below them. Unmuted channels have 00.</p><p>Use the ← and → keys to scroll to the muted channel and press m to unmute it.</p><p>The volume can be increased and decreased with the ↑ and ↓ keys.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Pacman Cheat Sheet For Ubuntu Users</title> <id>https://davidtsadler.com/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/index.html</id> @@ -237,7 +237,7 @@ $ pacman -Syu</code></pre><p>Since Arch uses a rolling release system there is n $ pacman -Scc</code></pre><p>Free up disk space by removing from the cache any packages that are no longer installed. Also removes any cached sync databases.</p><h2>Remove Used Dependencies</h2><pre><code class="shell">$ pacman --query --deps --unrequired --quiet | pacman --remove --recursive - -$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ pacman -Qdtq | pacman -Rs -</code></pre><p>Remove dependencies that are no longer needed, because e.g. the package which needed the dependencies was removed.</p><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing ST, DMENU and DWM in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/index.html</id> @@ -254,7 +254,7 @@ $ sudo make install</code></pre><h2>Configure and Install DMENU</h2><p>Again mov # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Again compiling and installing is done with the below commands.</p><pre><code class="shell">$ make clean $ sudo make install</code></pre><h2>Configure and Install DWM</h2><p>For the final time move to the directory created earlier.</p><pre><code class="shell">$ cd ~/.local/src/dwm</code></pre><p>As with dmenu the same edit needs to be made to the file config.mk.</p><pre><code class="shell">$ nvim config.mk</code></pre><pre><code class="vim"># XINERAMALIBS = -lXinerama # XINERAMAFLAGS = -DXINERAMA</code></pre><p>Compile and install as usual.</p><pre><code class="shell">$ make clean -$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +$ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed xorg-xinit I need to create a .xinitrc in my home folder.</p><pre><code class="shell">$ nvim ~/.xinitrc</code></pre><p>The contents of this file is just.</p><pre><code class="vim">exec dwm</code></pre><p>I can now start xorg and dwm with the below command.</p><pre><code class="shell">$ startx</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="https://dwm.suckless.org/">Dynamic Window Manager (DWM).</a><a href="https://suckless.org/">Suckless Software.</a><a href="https://st.suckless.org/">Simple Terminal.</a><a href="https://tools.suckless.org/dmenu/">DMenu.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Sudo: sorry, you must have a tty to run sudo</title> <id>https://davidtsadler.com/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.html</id> @@ -263,7 +263,7 @@ $ sudo make install</code></pre><h2>Starting DWM</h2><p>Since I have installed x <published>2020-07-13T12:00:00Z</published> <updated>2020-07-13T12:00:00Z</updated> <content>![CDATA[<h1>Sudo: sorry, you must have a tty to run sudo</h1><blockquote>Mon 13th July 2020 By David T. Sadler.</blockquote><p>I have found Deployer to be a great tool for deploying PHP applications. However when first setting out to use it I soon came across the error message 'sudo: sorry, you must have a tty to run sudo'. After some investigation I found that the error was triggered when Deployer was running commands via sudo. For those that don't know Deployer works by executing commands on your servers via ssh and depending on your server's configuration there could be issues when sudo is one of those commands.</p><h2>What is meant by 'sudo: sorry, you must have a tty to run sudo'?</h2><p>When sudo is executed the file /etc/sudoers is read to determine which users or groups can use sudo and what commands they can run. It actually does a bit more than that and you should read the manual for more information.</p><p>If you examine the sudoers file you will find that it contains the setting Defaults requiretty. This means that sudo can only be ran from a real tty. In other words if a user wants to run sudo they must have logged into a terminal before hand. This is normally a security feature so that sudo can't be ran from things such as cron jobs. However, it also means that you will have issues when running sudo from another machine via ssh as you also won't be logged into an actual terminal.</p><h2>How to resolve the issue?</h2><p>If you're happy to change the setting for all users simply use visudo to edit /etc/sudoers and change Defaults requiretty to Defaults !requiretty. Alternatively you can remove the tty requirement for a single user. In fact that is what I do when using Deployer. Since it connects to the server using a user called deployer I add the below configuration with visudo.</p><pre><code class="shell">Defaults:deployer !requiretty -deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Granting Sudo Access to a User in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/index.html</id> @@ -271,9 +271,9 @@ deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</c <author><name>David T. Sadler.</name></author> <published>2020-06-22T12:00:00Z</published> <updated>2020-06-22T12:00:00Z</updated> - <content>![CDATA[<h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers.</p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed.</li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd + <content>![CDATA[<h1>Granting Sudo Access to a User in Arch Linux</h1><blockquote>Mon 22nd 2020 By David T. Sadler.</blockquote><p>So by the end of my last post my minimal installation of Arch Linux had a user account that I could log into instead of the root user. However in order to do anything useful on the system I need to be able to run commands such as pacman that only the root user can do. Now one way to solve this is to change to the root user with su before running the command, but this defeats the point in creating a non-root user account in the first place. Instead a better way is to make use of sudo.</p><p>Sudo (su "do") gives the ability for a user (or groups of users) to run some (or all) commands as root and also provides an audit trail of the commands and their arguments. Usage is very simple, you enter sudo followed by the command that you want to run. For example,</p><pre><code class="shell">$ sudo pacman -Syu</code></pre><p>Configuration is done in the file /etc/sudoers. This is where you can specify which users or groups can use sudo and what commands they can run. However, you must be careful when editing this file as any syntax errors will make sudo unusable. Therefore it is strongly recommended to do any editing via the visudo command. This locks the sudoers file, saves edits to a temporary file, and checks that file's grammar before copying it to /etc/sudoers. </p><p>Traditionally in Linux systems users that should have privileged administrator rights are added to the wheel group which is then given sudo access. As the root user the first thing that I needed to do was add my user account to the wheel group with the usermod command.</p><pre><code class="shell">$ usermod -aG wheel david</code></pre><p>I used the below options with the command.</p><ul><li>-a Modifies the -G argument so that the user is added to the specified groups and not removed from any existing ones.</li><li>-G The list of supplementary groups that the user will be made a member of. In this case it's just wheel. Note that you need to pass -a otherwise the user will be removed from any group that is not listed. </li></ul><p>The sudo package then needed to be installed.</p><pre><code class="shell">$ pacman -S sudo</code></pre><p>Next I needed to grant sudo access to the wheel group by editing /etc/sudoers with visudo. Note that the default editor for visudo is vi. Since this has not been installed on my system I can change the editor to be nvim by first setting the variable EDITOR.</p><pre><code class="shell">$ EDITOR=nvim visudo</code></pre><p>Once the file was opened I located and uncommented the below line before saving and exiting nvim. This allows members of the wheel group to execute any command without having to enter their password.</p><pre><code class="shell">%wheel ALL(ALL) NOPASSWD: ALL</code></pre><p>I checked that I had sudo access by running the below command while logged into my user account.</p><pre><code class="shell">$ sudo pwd -/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +/home/david</code></pre><p>Since I wasn't prompted for my password and the command was executed I knew that I now had sudo access.</p><h3>Links</h3><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">Adding a User in Arch Linux.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Adding a User in Arch Linux</title> <id>https://davidtsadler.com/posts/arch/2020-06-15/adding-a-user-in-arch-linux/index.html</id> @@ -288,7 +288,7 @@ Retype new password: passwd: password updated successfully</code></pre><p>Now all I have to do is logout of the root account.</p><pre><code class="shell">$ logout</code></pre><p>Then login with the new credentials to confirm that everything is okay.</p><pre><code class="shell">suliman login: david Password: -david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-x220-thinkpad/">Installing Arch Linux on a Thinkpad X220.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Publishing Jigsaw Posts With Netlify Build Hooks</title> <id>https://davidtsadler.com/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.html</id> @@ -296,7 +296,7 @@ david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/instal <author><name>David T. Sadler.</name></author> <published>2020-06-08T12:00:00Z</published> <updated>2020-06-08T12:00:00Z</updated> - <content>![CDATA[<h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + <content>![CDATA[<h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Scheduling Posts in Jigsaw</title> <id>https://davidtsadler.com/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.html</id> @@ -309,16 +309,16 @@ david@suliman:$</code></pre><h3>Links</h3><a href="/posts/arch/2020-05-25/instal use Carbon\Carbon; return [ -'collections' => [ -'posts' => [ -'filter' => function ($item) { -$date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; -// Only publish posts that have a date and which is not in the future. -return $date ? $date <= Carbon::now() : false; -} -], -], -];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed.</p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + 'collections' => [ + 'posts' => [ + 'filter' => function ($item) { + $date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; + // Only publish posts that have a date and which is not in the future. + return $date ? $date <= Carbon::now() : false; + } + ], + ], +];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed. </p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Installing Arch Linux on a Thinkpad X220</title> <id>https://davidtsadler.com/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/index.html</id> @@ -326,7 +326,7 @@ return $date ? $date <= Carbon::now() : false; <author><name>David T. Sadler.</name></author> <published>2020-05-25T12:00:00Z</published> <updated>2020-05-25T12:00:00Z</updated> - <content>![CDATA[<h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal.</p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP.</p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk + <content>![CDATA[<h1>Installing Arch Linux on a Thinkpad X220</h1><blockquote>Mon 25th May 2020 By David T. Sadler.</blockquote><h2>Introduction</h2><p>So I purchased a used Thinkpad X220 for about £90 on eBay and decided to install Arch Linux onto it. This guide is the steps that I took to achieve this.</p><p>Now when it comes to Linux there is always more than one way of doing things and this guide is just my preferred way. Feel free to follow it for your own installation, just keep in mind that you may have to change some of the steps to suit your own circumstances. Also there is every chance that the information presented here will be out of date so I recommend that you at least read the through the official installation guide for the most up-to-date information.</p><h2>Download the Arch Linux ISO Image</h2><p>The Arch Linux download page provides direct download and torrent links. You should also download the PGP signature to the same location and use the gpg command to verify that the ISO has not been compromised.</p><pre><code class="shell">$ gpg --keyserver-options auto-key-retrieve --verify archlinux-2020.05.01-x86_64.iso.sig</code></pre><h2>Create a Live USB of Arch Linux</h2><p>Flash the image to a USB drive using Etcher. Alternatively you can use the dd command. Just ensure that /path/to/archlinux.iso is to where you have downloaded the image and that /dev/sdx is your USB drive.</p><pre><code class="shell">dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync</code></pre><h2>Boot the Live Environment</h2><p>I wanted to make sure that the Thinkpad was using UEFI as I would be using EFISTUB to load the Linux kernel as an EFI executable. This is done through the BIOS which can be gotten to by pressing the ThinkVantage button as the machine is booting before pressing F1 to get to the BIOS settings. From there navigate to Startup and changed the UEFI/Legacy Boot option to be UEFI Only. Press F10 to save and exit the BIOS and then power down the machine.</p><p>With the USB drive plugged in power the machine back on, all the while pressing F12 until the boot menu appears and select USB HDD: Mass Storage Device and wait for the installation image to boot. When prompted select Arch Linux archiso X86_64 UEFI CD where you will be take to the live environment's terminal. </p><h2>Set the Keyboard Layout</h2><p>The default console map is US which meant that for me pressing Shift+3 was displaying the hash symbol (#) instead of the pound symbol (£). So the UK keyboard layout needed to be loaded.</p><pre><code class="shell">$ loadkeys uk</code></pre><p>You can get a list of supported keyboard layouts if you need to load a different one.</p><pre><code class="shell">ls /usr/share/kbd/keymaps/**/*.map.gz</code></pre><h2>Verify the Boot Mode</h2><p>To verify that the Thinkpad has UEFI enabled check that the efivars directory exists.</p><pre><code class="shell">$ ls /sys/firmware/efi/efivars</code></pre><h2>Connect to the Internet</h2><p>Verify that the machine can connect to the internet with the ping command.</p><pre><code class="shell">$ ping -c3 davidtsadler.com</code></pre><p>Before booting the machine I plugged in an Ethernet cable that was connected directly to my home network's router. The installation environment detected the network connection and obtained an IP address via DHCP. </p><h2>Update the System Clock</h2><p>Ensure the system clock is correct.</p><pre><code class="shell">$ timedatectl set-ntp true</code></pre><h2>Partition the Disks</h2><p>Use the lsblk command to determine which disks and partitions exist on the system.</p><pre><code class="shell">$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 535M 1 loop /run/archiso/sfs/airootfs @@ -348,7 +348,7 @@ sdb 8:16 1 7.4G 0 disk +-------------------+ +--------------------------------------------------------+</code></pre><p>The hard drive would be split into two partitions. The first, sdb1 would be 512MB in size and mounted in the file system at /boot. This would be the EFI system partition. The reminder of the disk space would be given to the partition sda2 and encrypted using LUKS2. LVM would then be used to create the volume group vg0 that would be divided into three partitions as logical volumes.</p><ul><li>/dev/vg0/root 50G root partition.</li><li>/dev/vg0/swap 16G swap partition.</li><li>/dev/vg0/home 200G home partition.</li></ul><p>Use fdisk to create the partitions.</p><pre><code class="shell">$ fdisk /dev/sda</code></pre><p>Enter g to create a new empty GPT partition table</p><pre><code class="shell">Command (m for help): g Created a new GPT disklabel (GUID: 6987D065-936E-1547-9F02-F78145025A96).</code></pre><p>Since this is a UEFI system there must be a EFI partition at the beginning of the disk. Enter n to add a new partition and enter 1 to assign it as the first partition. Use the default value for the first sector but enter +512M for the last sector.</p><pre><code class="shell">Command (m for help): n Partition number (1-128, default 1): 1 -First sector (2048-625142414, default 2048): +First sector (2048-625142414, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-625142414, default 625142414): +512M Created a new partition 1 of type 'Linux filesystem' and of size 512 MiB.</code></pre><p>Enter t to change the partition type and enter 1 to make it an EFI System. You can also get a list of partition types by pressing L.</p><pre><code class="shell">Command (m for help): t @@ -356,8 +356,8 @@ Selected partition 1 Partition type (type L to list all types): 1 Changed type of partition 'Linux filesystem' to 'EFI System'.</code></pre><p>To create the second partition enter n again to add another partition, and then enter 2 to assign it as the second partition. Use the default values for both first and last sectors to allocate the remainder of the drive.</p><pre><code class="shell">Command (m for help): n Partition number (2-128, default 2): 2 -First sector (1050624-625142414, default 1050624): -Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): +First sector (1050624-625142414, default 1050624): +Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-625142414, default 625142414): Created a new partition 2 of type 'Linux filesystem' and of size 297.6 GiB.</code></pre><p>Enter w to write the changes and quit.</p><pre><code class="shell">Command (m for help): w The partition table has been altered. @@ -367,40 +367,40 @@ Syncing disks.</code></pre><p>Use lsblk to confirm that two partitions have been NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 298.1G 0 disk ├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command.</p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition.</p><pre><code class="shell">WARNING! +└─sda2 8:2 0 297.6G 0 part</code></pre><h2>LUKS</h2><p>Encrypt the second partition with the cryptsetup command. </p><pre><code class="shell">$ cryptsetup luksFormat /dev/sda2</code></pre><p>When prompted enter YES in capitals to overwrite any data that is currently on the partition. </p><pre><code class="shell">WARNING! ======== This will overwrite data on /dev/sda2 irrevocably. -Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: -Verify passphrase: -cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Are you sure? (Type 'yes' in capital letters): YES</code></pre><p>Enter and verify a passphrase. Whenever the machine is now booted you will be prompted to enter this passphrase in order for the partition to be decrypted.</p><pre><code class="shell">Enter passphrase for /dev/sda2: +Verify passphrase: +cryptsetup luksFormat /dev/sda2 17.01s user 1.05s system 105% cpu 17.106 total</code></pre><h2>LVM on LUKS</h2><p>Before setting up LVM decrypt the partition.</p><pre><code class="shell">$ cryptsetup open /dev/sda2 cryptlvm</code></pre><p>You will be prompted to enter the passphrase that you set up earlier.</p><pre><code class="shell">Enter passphrase for /dev/sda2: cryptsetup open /dev/sda2 cryptlvm 6.48s user 0.36s system 92% cpu 7.436 total</code></pre><p>Create a physical volume.</p><pre><code class="shell">$ pvcreate /dev/mapper/cryptlvm</code></pre><p>Create a volume group called vg0.</p><pre><code class="shell">$ vgcreate vg0 /dev/mapper/cryptlvm</code></pre><p>Create three logical volumes for the root, swap and home partitions.</p><pre><code class="shell">$ lvcreate -L 50G vg0 -n root $ lvcreate -L 16G vg0 -n swap $ lvcreate -L 200G vg0 -n home</code></pre><p>Make use of lsblk again to verify that LVM has been setup as expected.</p><pre><code class="shell">$ lsblk /dev/sda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT -sda 8:0 0 298.1G 0 disk -├─sda1 8:1 0 512M 0 part -└─sda2 8:2 0 297.6G 0 part -└─cryptlvm 254:0 0 297.6G 0 crypt -├─vg0-root 254:1 0 50G 0 lvm -├─vg0-swap 254:2 0 16G 0 lvm -└─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root +sda 8:0 0 298.1G 0 disk +├─sda1 8:1 0 512M 0 part +└─sda2 8:2 0 297.6G 0 part + └─cryptlvm 254:0 0 297.6G 0 crypt + ├─vg0-root 254:1 0 50G 0 lvm + ├─vg0-swap 254:2 0 16G 0 lvm + └─vg0-home 254:3 0 200G 0 lvm</code></pre><h2>Format the Partitions</h2><p>Format the boot partition at /dev/sda1 with a FAT32 file system as the UEFI specification requires the usage of it.</p><pre><code class="shell">$ mkfs.fat -F32 /dev/sda1</code></pre><p>The root and home partitions can be formatted with ext4.</p><pre><code class="shell">$ mkfs.ext4 /dev/vg0/root $ mkfs.ext4 /dev/vg0/home</code></pre><p>Initialise the swap partition.</p><pre><code class="shell">$ mkswap /dev/vg0/swap -$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot.</p><pre><code class="shell">$ mkdir /mnt/boot +$ swapon /dev/vg0/swap</code></pre><h2>Mount the File Systems</h2><p>Mount the root partition into /mnt.</p><pre><code class="shell">$ mount /dev/vg0/root /mnt</code></pre><p>Mount the boot partition into /mnt/boot. </p><pre><code class="shell">$ mkdir /mnt/boot $ mount /dev/sda1 /mnt/boot</code></pre><p>Finally mount the home partition into /mnt/home.</p><pre><code class="shell">$ mkdir /mnt/home -$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting.</li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost +$ mount /dev/vg0/home /mnt/home</code></pre><h2>Select the Mirrors</h2><p>All mirror servers defined in /etc/pacman.d/mirrorlist where done at the time the installation image was built. Since it's ideal to try and use servers that are close to your location you can rebuild the list using the rankmirrors utility. This is not included by default on the live environment so you will need to download it.</p><p>First sync the pacman repository.</p><pre><code class="shell">pacman -Syy</code></pre><p>Then download the pacmain-contrib package which contains the rankmirrors utility.</p><pre><code class="shell">$ pacman -S pacman-contrib</code></pre><p>The official Pacman Mirrorlist Generator can be used to get an up-to-date list of servers for your country. The below command obtains a list of UK servers that support https and pass it to rankmirrors to obtain the 5 fastest.</p><pre><code class="shell">$ curl -s "https://www.archlinux.org/mirrorlist/?country=GB&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 - > /etc/pacman.d/mirrorlist</code></pre><h2>Install Essential Packages</h2><p>The pacstrap script is used to install the base package, Linux kernel and firmware.</p><pre><code class="shell">$ pacstrap /mnt base linux linux-firmware neovim wpa_supplicant dhcpcd cryptsetup lvm2 efibootmgr intel-ucode</code></pre><p>I also installed a few other packages that I knew I was going to need.</p><ul><li>neovim. Allows you to edit files instead of using nano.</li><li>wpa_supplicant. Provides tools for connecting to a WPA2 protected wireless network.</li><li>dhcpcd. Needed so that you machine can obtain an IP address from your home router via dhcp.</li><li>cryptsetup. Since the partition is encrypted this package is required in order for it to be decrypted during booting. </li><li>lvm2. Provides the LVM tools to manage the LVM partition.</li><li>efibootmgr. Needed to configure the system to boot via UEFI.</li><li>intel-ucode. Enables microcode updates during boot.</li></ul><h2>Fstab</h2><p>Create a fstab file on the new system.</p><pre><code class="shell">$ genfstab -U /mnt >> /mnt/etc/fstab</code></pre><h2>Chroot</h2><p>Use arch-chroot to enter the new system as the root user. From now on you will be configuring the new system.</p><pre><code class="shell">$ arch-chroot /mnt</code></pre><h2>Time Zone</h2><p>Setup the timezone. Replace Europe/London with your timezone.</p><pre><code class="shell">$ ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime</code></pre><p>Update the hardware clock.</p><pre><code class="shell">$ hwclock --systohc</code></pre><h2>Localization</h2><p>Use nvim to edit /etc/locale.gen.</p><pre><code class="shell">$ nvim /etc/locale.gen</code></pre><p>Uncomment your preferred language. For me this meant en_GB.UTF-8 UTF-8. Save the file and exit before generating the locales.</p><pre><code class="shell">$ locale-gen</code></pre><p>Edit /etc/locale.conf.</p><pre><code class="shell">$ nvim /etc/locale.conf</code></pre><p>Add the below line. Replace en_GB.UTF-8 with the language that you chose earlier.</p><pre>LANG=en_GB.UTF-8</pre><p>If you used loadkeys earlier you will need to edit /etc/vconsole.conf and add your chosen keymap.</p><pre><code class="shell">$ nvim /etc/vconsole.conf</code></pre><p>For me this meant adding the UK keymap.</p><pre>KEYMAP=uk</pre><h2>Network Configuration</h2><p>Create the file /etc/hostname and add an entry to it. This hostname will be the name of the machine on your network. I tend to name by devices after characters from the book Howl's Moving Castle https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle).</p><pre><code class="shell">$ echo suliman > /etc/hostname</code></pre><p>You then need to edit the /etc/hosts file.</p><pre><code class="shell">$ nvim /etc/hosts</code></pre><p>Add the following lines to this file. Replace suliman with the hostname you set up earlier.</p><pre><code class="shell">127.0.0.1 localhost ::1 localhost 127.0.0.1 suliman.localdomain suliman</code></pre><h2>Wireless</h2><p>Use the ip command to determine the name of the wireless network interface.</p><pre><code class="shell">$ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 -link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 -link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff + link/ether f0:de:f1:86:e1:75 brd ff:ff:ff:ff:ff:ff 3: wwp0s29u1u4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff + link/ether 86:06:37:c4:9b:41 brd ff:ff:ff:ff:ff:ff 4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 -link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 + link/ether 08:11:96:02:10:ac brd ff:ff:ff:ff:ff:ff</code></pre><p>Looking at the output of the ip command this is wlan0. This name however will not be the name of the interface once the installation has been completed. You see the Arch installation environment does not use predictable names for interfaces. This is due to it using iwd which is unable to cope with interface renaming and so it is disabled for wireless interfaces. When the system boots into the installed system predictable names for interfaces will be enabled and wlan0 will be assigned a different name.</p><p>In order to find out what name will be assigned use the udevadm command.</p><pre><code class="shell">$ udevadm test-builtin net_id /sys/class/net/wlan0 Load module index Parsed configuration file /usr/lib/systemd/network/99-default.link @@ -412,17 +412,17 @@ ID_NET_NAME_MAC=wlx0811960210ac ID_OUI_FROM_DATABASE=Intel Corporate ID_NET_NAME_PATH=wlp3s0 Unload module index -Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line.</p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command.</p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead.</p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments.</p><pre>#!/bin/sh +Unloaded link configuration context.</code></pre><p>What we are interested in is the value of ID_NET_NAME_PATH which is wlp3s0.</p><p>My wireless network is a WPA2 protected network with a hidden SSID. Since wpa_supplicant has been installed when running pacstrap it is possible to use wpa_passphrase to generate the configuration file that wpa_supplicant will use to connect to the wireless network. Replace &lt;SSID&gt; and &lt;PASSWORD&gt; with your details. Note that the name of the configuration file contains the name of the network interface wlp3s0. Replace this with the name of your network interface.</p><pre><code class="shell">$ wpa_passphrase <SSID> <PASSWORD> > /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>If your wireless network uses a hidden SSID you will need to edit the configuration file.</p><pre><code class="shell">$ nvim /etc/wpa_supplicant/wpa_supplicant-wlp3s0.conf</code></pre><p>And add the below line. </p><pre><code class="shell">scan_ssid=1</code></pre><p>Make sure that wpa_supplicant starts at boot.</p><pre><code class="shell">$ systemctl enable wpa_supplicant@wlp3s0.service</code></pre><p>Have an IP address assigned via DHCP during booting.</p><pre><code class="shell">$ systemctl enable dhcpcd@wlp3s0.service</code></pre><h2>Initramfs</h2><p>You will need to rebuild the initial ramdisk and the current one is not aware that the filesystem will be encrypted. Before rebuilding it some configuration changes need to be made.</p><pre><code class="shell">$ nvim /etc/mkinitcpio.conf</code></pre><p>Locate the section where the HOOKS are configured and replace it with the line below.</p><pre>HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)</pre><p>This ensures that the keyboard is present before the filesystem is detected so that you are enter the passphrase to decrypt the partition. It also ensures that the decryption is done before the LVM is handled.</p><p>Save the changes and exist before rebuilding with the mkinitcpio command. </p><pre><code class="shell">mkinitcpio -P</code></pre><h2>EFISTUB Booting and Microcode</h2><p>The Thinkpad X220 UEFI implementation allows an operating system to be booted without the need for an intermediate bootloader such as GRUB. It is possible to add a UEFI boot entry to the motherboard itself and have Arch booted directly.</p><p>Modifying the motherboard boot entries is done using efibootmgr. However usage of this command can be quite verbose so it is recommended to create a shell script instead. </p><pre><code class="shell">nvim /usr/local/sbin/mkefibootentry</code></pre><p>The shell script will call efibootmgr with the required arguments. </p><pre>#!/bin/sh # Determine the UUID of the partition that is encrypted PARTUUID=`blkid /dev/sda2 -s PARTUUID -o value` efibootmgr \ ---disk /dev/sda --part 1 \ ---create --label "Arch Linux" \ ---loader /vmlinuz-linux \ ---unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ ---verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition.</p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject.</p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + --disk /dev/sda --part 1 \ + --create --label "Arch Linux" \ + --loader /vmlinuz-linux \ + --unicode 'cryptdevice=PARTUUID='$PARTUUID':cryptlvm root=/dev/vg0/root rw initrd=\intel-ucode.img initrd=\initramfs-linux.img' \ + --verbose </pre><p>The --unicode argument is where the kernel parameters are specified. This tells the system that the partition identified by PARTUUID is encrypted and that the root filesystem to mount is the logical volume called root that is part of the volume group vg0. The microcode is also loaded with initrd=\intel-ucode.img.</p><p>Make this script executable.</p><pre><code class="shell">chmod u+x /usr/local/sbin/mkefibootentry</code></pre><p>Run the script to add to the motherboard boot entries.</p><pre><code class="shell">$ mkefibootentry</code></pre><h2>Root Password</h2><p>Create a secure password for the root user.</p><pre><code class="shell">$ passwd</code></pre><h2>Reboot</h2><p>Return to the Arch live installation environment.</p><pre><code class="shell">$ exit</code></pre><p>Unmount the partitions.</p><pre><code class="shell">$ umount -R /mnt</code></pre><p>Restart the machine with reboot. Remember to remove any installation media such as a USB drive.</p><pre><code class="shell">$ reboot</code></pre><p>Provided nothing has gone wrong your machine will boot into a fresh installation of Arch Linux. Don't forget that during the boot you will be prompted to enter the passphrase to decrypt the system partition. </p><p>Following this guide will leave you with a very minimal system where you can login as the root user. From this point how you configure the system is up to you as it will be very different to how I configure my own. If you interested in seeing how I do it then see my other posts on the subject. </p><h3>Links</h3><a href="https://www.archlinux.org/Installation_guide/">Offical Arch Installation Guide.</a><a href="https://www.archlinux.org/download/">Arch Linux Download Page.</a><a href="https://www.balena.io/etcher/">Etcher.</a><a href="https://www.archlinux.org/mirrorlist/">Pacman Mirrorlist Generator.</a><a href="https://en.wikipedia.org/wiki/Howl%27s_Moving_Castle/">Wikipedia Entry for Howl's Moving Castle.</a><a href="/posts/arch/">Arch - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">Creating an Ebook With Markdown</title> <id>https://davidtsadler.com/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.html</id> @@ -433,12 +433,12 @@ efibootmgr \ <content>![CDATA[<h1>Creating an Ebook With Markdown</h1><blockquote>Mon 30th March 2020 By David T. Sadler.</blockquote><p>Pandoc is a great tool for converting a file in one markup format into another. This means we can use it to convert a file written in Markdown into an EPUB file that is supported by many e-readers.</p><p>Lets start by writting a very simple markdown file called example_ebook.md.</p><pre><code class="markdown">--- title: - type: main -text: Example Ebook + text: Example Ebook - type: subtitle -text: An Ebook created from a Markdown file + text: An Ebook created from a Markdown file creator: - role: author -text: David Sadler + text: David Sadler publisher: Published by myself --- @@ -466,6 +466,6 @@ This is the second paragraph of chapter 2. This is the first paragraph of chapter 3. -This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.html b/www/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.html index f92d177..b918199 100644 --- a/www/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.html +++ b/www/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>How to Host Your Own Gemini Site in the Cloud</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -28,12 +31,12 @@ $ ufw enable</code></pre><h3>Harden SSH</h3><p>I edited the /etc/ssh/sshd_config PasswordAuthentication no</pre><p>Since I'd made changes to the configuration I needed to restart the SSH service.</p><pre><code class="shell">$ service sshd restart</code></pre><h3>Create non-root User</h3><p>Whenever I access a server I like to login as a non-root user that is able to run sudo on the system.</p><pre><code class="shell">$ adduser gemini $ usermod -aG sudo gemini</code></pre><p>As the SSH key is already on the server I can copy it to the non-root user account.</p><pre><code class="shell">$ rsync --archive --chown=gemini:gemini ~/.ssh /home/gemini</code></pre><p>On my local system I confirm that I can log in as the new user without a password.</p><pre><code class="shell">$ ssh gemini@davidtsadler.co.uk -i ~/.ssh/davidtsadler.co.uk/id_rsa</code></pre><p>I also confirm that I have sudo access.</p><pre><code class="shell">$ sudo ls</code></pre><h2>Installing a Gemini Site and Server</h2><h3>Directory structure</h3><p>I decided to go with a very simple directory structure. Each site will be a sub-directory in ~/sites that will be named after the domain name. Then each site will have the following sub-directories. The idea is that I may want to host more than one site in the future.</p><ul><li>bin This will contain the Gemini server binary.</li><li>certs TLS certificates for the site are kept here.</li><li>public This will contain the .gmi files of the site.</li><li>scripts Contains scripts used to start the Gemini server.</li></ul><p>I created the directory structure with the below command.</p><pre><code class="shell">$ mkdir -p ~/sites/davidtsadler.co.uk/{bin,certs,public,scripts}</code></pre><h3>Install certificates</h3><p>Sine the Gemini protocol encourages using a self-signed certificate I installed one with the openssl command.</p><pre><code class="shell">$ openssl req -x509 \ --newkey rsa:4096 \ --keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ --out ~/sites/davidtsadler.co.uk/certs/cert.pem \ --days 3650 \ --nodes \ --subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi + -newkey rsa:4096 \ + -keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ + -out ~/sites/davidtsadler.co.uk/certs/cert.pem \ + -days 3650 \ + -nodes \ + -subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi # Welcome Hello world! @@ -49,13 +52,13 @@ $ chmod u+x agate</code></pre><p>I wrote a very simple bash script to run agate #!/bin/bash /home/gemini/sites/davidtsadler.co.uk/bin/agate \ ---content /home/gemini/sites/davidtsadler.co.uk/public/ \ ---key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ ---cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ ---addr [::]:1965 \ ---addr 0.0.0.0:1965 \ ---hostname davidtsadler.co.uk \ ---lang en-GB + --content /home/gemini/sites/davidtsadler.co.uk/public/ \ + --key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ + --cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ + --addr [::]:1965 \ + --addr 0.0.0.0:1965 \ + --hostname davidtsadler.co.uk \ + --lang en-GB EOF $ chmod u+x ~/sites/davidtsadler.co.uk/scripts/start</code></pre><h3>Testing the Site</h3><p>At this point I have the Gemini server installed and a site available for testing.</p><p>I first started agate with the bash script.</p><pre><code class="shell">$ ~/sites/davidtsadler.co.uk/scripts/start @@ -75,6 +78,8 @@ WantedBy=default.target</pre><p>I then started this service and confirmed it was $ sudo systemctl status agate.service -Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/gemini/atom.xml b/www/posts/gemini/atom.xml index 79ac301..7359329 100644 --- a/www/posts/gemini/atom.xml +++ b/www/posts/gemini/atom.xml @@ -22,12 +22,12 @@ $ ufw enable</code></pre><h3>Harden SSH</h3><p>I edited the /etc/ssh/sshd_config PasswordAuthentication no</pre><p>Since I'd made changes to the configuration I needed to restart the SSH service.</p><pre><code class="shell">$ service sshd restart</code></pre><h3>Create non-root User</h3><p>Whenever I access a server I like to login as a non-root user that is able to run sudo on the system.</p><pre><code class="shell">$ adduser gemini $ usermod -aG sudo gemini</code></pre><p>As the SSH key is already on the server I can copy it to the non-root user account.</p><pre><code class="shell">$ rsync --archive --chown=gemini:gemini ~/.ssh /home/gemini</code></pre><p>On my local system I confirm that I can log in as the new user without a password.</p><pre><code class="shell">$ ssh gemini@davidtsadler.co.uk -i ~/.ssh/davidtsadler.co.uk/id_rsa</code></pre><p>I also confirm that I have sudo access.</p><pre><code class="shell">$ sudo ls</code></pre><h2>Installing a Gemini Site and Server</h2><h3>Directory structure</h3><p>I decided to go with a very simple directory structure. Each site will be a sub-directory in ~/sites that will be named after the domain name. Then each site will have the following sub-directories. The idea is that I may want to host more than one site in the future.</p><ul><li>bin This will contain the Gemini server binary.</li><li>certs TLS certificates for the site are kept here.</li><li>public This will contain the .gmi files of the site.</li><li>scripts Contains scripts used to start the Gemini server.</li></ul><p>I created the directory structure with the below command.</p><pre><code class="shell">$ mkdir -p ~/sites/davidtsadler.co.uk/{bin,certs,public,scripts}</code></pre><h3>Install certificates</h3><p>Sine the Gemini protocol encourages using a self-signed certificate I installed one with the openssl command.</p><pre><code class="shell">$ openssl req -x509 \ --newkey rsa:4096 \ --keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ --out ~/sites/davidtsadler.co.uk/certs/cert.pem \ --days 3650 \ --nodes \ --subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi + -newkey rsa:4096 \ + -keyout ~/sites/davidtsadler.co.uk/certs/key.rsa \ + -out ~/sites/davidtsadler.co.uk/certs/cert.pem \ + -days 3650 \ + -nodes \ + -subj "/CN=davidtsadler.co.uk"</code></pre><h3>Create Some Test Content</h3><p>I created a very simple index.gmi file purely for testing.</p><pre><code class="shell">$ cat << EOF > ~/sites/davidtsadler.co.uk/public/index.gmi # Welcome Hello world! @@ -43,13 +43,13 @@ $ chmod u+x agate</code></pre><p>I wrote a very simple bash script to run agate #!/bin/bash /home/gemini/sites/davidtsadler.co.uk/bin/agate \ ---content /home/gemini/sites/davidtsadler.co.uk/public/ \ ---key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ ---cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ ---addr [::]:1965 \ ---addr 0.0.0.0:1965 \ ---hostname davidtsadler.co.uk \ ---lang en-GB + --content /home/gemini/sites/davidtsadler.co.uk/public/ \ + --key /home/gemini/sites/davidtsadler.co.uk/certs/key.rsa \ + --cert /home/gemini/sites/davidtsadler.co.uk/certs/cert.pem \ + --addr [::]:1965 \ + --addr 0.0.0.0:1965 \ + --hostname davidtsadler.co.uk \ + --lang en-GB EOF $ chmod u+x ~/sites/davidtsadler.co.uk/scripts/start</code></pre><h3>Testing the Site</h3><p>At this point I have the Gemini server installed and a site available for testing.</p><p>I first started agate with the bash script.</p><pre><code class="shell">$ ~/sites/davidtsadler.co.uk/scripts/start @@ -69,6 +69,6 @@ WantedBy=default.target</pre><p>I then started this service and confirmed it was $ sudo systemctl status agate.service -Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +Active: active (running)</pre><p>The final step was to have this service start when the system is rebooted.</p><pre>$ sudo systemctl enable agate.service</pre><h2>Conclusion</h2><p>Setting up a Gemini site was easy to do and I hope this guide shows it. I have several ideas about how I'm going to use this new site and I'm excited to see where this leads to.</p><h3>Links</h3><a href="https://www.gandi.net/">Gandi.net - My domain registrar of choice.</a><a href="https://hetzner.cloud/?ref=Gf3UFbRaixBK">Hetzner - My cloud server provider.</a><a href="https://github.com/mbrubeck/agate/">Agate - A simple Gemini server.</a><a href="gemini://davidtsadler.co.uk/">davidtsadler.co.uk - My Gemini site.</a><a href="/posts/gemini/">Gemini - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/gemini/index.html b/www/posts/gemini/index.html index 7e4cfe9..ccf86af 100644 --- a/www/posts/gemini/index.html +++ b/www/posts/gemini/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Gemini</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Gemini</h1><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Gemini</h1><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/index.html b/www/posts/index.html index 669dc92..95f37a1 100644 --- a/www/posts/index.html +++ b/www/posts/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts</h1><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts</h1><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><a href="/posts/gemini/2021-02-08/how-to-host-your-own-gemini-site-in-the-cloud/">2021-02-08 - How to Host Your Own Gemini Site in the Cloud</a><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><a href="/posts/arch/2020-09-07/installing-zsh-and-powerlevel10k-on-arch-linux/">2020-09-07 - Installing Zsh and Powerlevel10k on Arch Linux</a><a href="/posts/arch/2020-08-31/enabling-audio-in-arch-linux/">2020-08-31 - Enabling Audio in Arch Linux</a><a href="/posts/arch/2020-08-24/pacman-cheat-sheet-for-ubuntu-users/">2020-08-24 - Pacman Cheat Sheet For Ubuntu Users</a><a href="/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/">2020-08-17 - Installing ST, DMENU and DWM in Arch Linux</a><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><a href="/posts/arch/2020-06-22/granting-sudo-access-to-a-user-in-arch-linux/">2020-06-22 - Granting Sudo Access to a User in Arch Linux</a><a href="/posts/arch/2020-06-15/adding-a-user-in-arch-linux/">2020-06-15 - Adding a User in Arch Linux</a><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="/posts/arch/2020-05-25/installing-arch-linux-on-a-thinkpad-x220/">2020-05-25 - Installing Arch Linux on a Thinkpad X220</a><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.html b/www/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.html index e0f467b..9438359 100644 --- a/www/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.html +++ b/www/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Scheduling Posts in Jigsaw</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -23,15 +26,17 @@ use Carbon\Carbon; return [ -'collections' => [ -'posts' => [ -'filter' => function ($item) { -$date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; -// Only publish posts that have a date and which is not in the future. -return $date ? $date <= Carbon::now() : false; -} -], -], -];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed.</p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + 'collections' => [ + 'posts' => [ + 'filter' => function ($item) { + $date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; + // Only publish posts that have a date and which is not in the future. + return $date ? $date <= Carbon::now() : false; + } + ], + ], +];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed. </p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/jigsaw/atom.xml b/www/posts/jigsaw/atom.xml index 1929fae..5719a42 100644 --- a/www/posts/jigsaw/atom.xml +++ b/www/posts/jigsaw/atom.xml @@ -17,15 +17,15 @@ use Carbon\Carbon; return [ -'collections' => [ -'posts' => [ -'filter' => function ($item) { -$date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; -// Only publish posts that have a date and which is not in the future. -return $date ? $date <= Carbon::now() : false; -} -], -], -];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed.</p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + 'collections' => [ + 'posts' => [ + 'filter' => function ($item) { + $date = $item->date ? Carbon::createFromFormat('U', $item->date) : null; + // Only publish posts that have a date and which is not in the future. + return $date ? $date <= Carbon::now() : false; + } + ], + ], +];</code></pre><p>When deploying the site each post is passed to this filter. The first thing it does is convert the date that has been specified in the post's YAML front matter into a Carbon instance. It then returns true if the date is on or before the current date, I.e. when the site is been deployed. </p><p>With this filter I can specify future dates for several posts and they will only published once that date comes around. Posts are also exluded if a date has not been specified. This allows me to have posts that are a work in progress and shouldn't be published.</p><h3>Links</h3><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://jigsaw.tighten.co/docs/collections-filtering/">Using Filters in Jigsaw.</a><a href="https://carbon.nesbot.com/">Carbon - PHP API Extension for DateTime.</a><a href="/posts/jigsaw/">Jigsaw - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/jigsaw/index.html b/www/posts/jigsaw/index.html index e80ae7b..9d2b42f 100644 --- a/www/posts/jigsaw/index.html +++ b/www/posts/jigsaw/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Jigsaw</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Jigsaw</h1><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Jigsaw</h1><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html b/www/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html index 0cb27d1..88ed736 100644 --- a/www/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html +++ b/www/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -22,21 +25,23 @@ Illuminate\Database\QueryException -SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') + SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') -at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 -674▕ // If an exception occurs when attempting to run a query, we'll format the error -675▕ // message to include the bindings with SQL, which will make this exception a -676▕ // lot more helpful to the developer instead of just the database's errors. -677▕ catch (Exception $e) { -678▕ throw new QueryException( -679▕ $query, $this->prepareBindings($bindings), $e -680▕ ); -681▕ } -682▕ + at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 + 674▕ // If an exception occurs when attempting to run a query, we'll format the error + 675▕ // message to include the bindings with SQL, which will make this exception a + 676▕ // lot more helpful to the developer instead of just the database's errors. + 677▕ catch (Exception $e) { + 678▕ throw new QueryException( + 679▕ $query, $this->prepareBindings($bindings), $e + 680▕ ); + 681▕ } + 682▕ -+33 vendor frames -34 artisan:37 -Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + +33 vendor frames + 34 artisan:37 + Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.html b/www/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.html index f85bf66..7bc316e 100644 --- a/www/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.html +++ b/www/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Installing Laravel Homestead in Arch Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -24,22 +27,22 @@ $ git checkout release</code></pre><p>Create the Homestead.yaml file by using th $ echo "<?php phpinfo();" > ~/projects/testsite/public/index.php</code></pre><h3>Setting up SSH</h3><p>I like to use unique ssh keys for servers that I connect to and that includes any virtual machines running on my local machine. The ssh-keygen command generates a new key that I store in a directory separate from my other ones.</p><pre><code class="shell">$ mkdir ~/.ssh/homestead -$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site.</p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file.</p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead +$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site. </p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file. </p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ nvim Homestead.yaml</code></pre><p>First tell Vagrant that Virtualbox will be providing the virtual machine.</p><pre><code class="yaml">set provider: virtualbox</code></pre><p>Vagrant needs to setup the ssh keys between the host and the guest so that you can connect via ssh. Enter the path to the one created earlier.</p><pre><code class="yaml">authorize: ~/.ssh/homestead/id_rsa.pub keys: -- ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: -- map: ~/projects/testsite -to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: -- map: testsite.local -to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: -- testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: -- mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead + - ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: + - map: ~/projects/testsite + to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: + - map: testsite.local + to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: + - testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: + - mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ vagrant up</code></pre><p>Once the machine is booted I can browse to http://testsite.local/ to see the simple site that is now served by Homestead.</p><h2>Installing a Laravel Site</h2><p>Now that Homestead is installed and serving a simple site its time to move onto installing the first Laravel application. Since Homestead provides all the tools required to do this the first thing to do is connect to the virtual machine.</p><pre><code class="shell">$ cd ~/.local/share/homestead -$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell ">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +--------------------+ | Database | @@ -69,7 +72,9 @@ DB_PORT=3306 DB_DATABASE=testsite DB_USERNAME=testsite DB_PASSWORD=testsite</pre><p>Now when you browse to http://testsite.local you will see the Laravel welcome page.</p><h2>Simplified SSH</h2><p>I prefer to just use the host system's ssh command to connect to Homestead as it cuts out having to navigate to the Homestead directory and running vagrant ssh.</p><p>To simplify ssh I first add a hostname for the virtual machine to the file /etc/hosts/</p><pre><code class="shell">192.168.10.10 homestead</code></pre><p>I then edit ~/.ssh/config and add the below configuration. This tells ssh to automatically use the keys and username specified when connecting to the virtual machine.</p><pre><code class="ssh">Host homestead -IdentityFile ~/.ssh/homestead/id_rsa -User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + IdentityFile ~/.ssh/homestead/id_rsa + User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/laravel/atom.xml b/www/posts/laravel/atom.xml index 1bc5601..621f985 100644 --- a/www/posts/laravel/atom.xml +++ b/www/posts/laravel/atom.xml @@ -18,22 +18,22 @@ $ git checkout release</code></pre><p>Create the Homestead.yaml file by using th $ echo "<?php phpinfo();" > ~/projects/testsite/public/index.php</code></pre><h3>Setting up SSH</h3><p>I like to use unique ssh keys for servers that I connect to and that includes any virtual machines running on my local machine. The ssh-keygen command generates a new key that I store in a directory separate from my other ones.</p><pre><code class="shell">$ mkdir ~/.ssh/homestead -$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site.</p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file.</p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead +$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/homestead/id_rsa</code></pre><h3>Hostname Resolution</h3><p>Since I am using one instance of Homestead for multiple sites I need to configure the host machine so that requests are directed to the correct site on the virtual machine. This is done by adding an entry into the /etc/hosts file for each site. </p><p>First I need to know the IP address of the virtual machine. This can be done by looking in the Homestead.yaml file for the ip entry.</p><pre><code class="yaml">ip: "192.168.10.10"</code></pre><p>Then for each site that will be hosted on the virtual machine add it's domain and ip to the /etc/hosts file. </p><pre><code class="shell">$ sudo nvim /etc/hosts</code></pre><pre><code class="shell">192.168.10.10 testsite.local</code></pre><h2>Configuring Homestead</h2><p>Homestead is configured by editing the Homestead.yaml file that was created with the init.sh command earlier.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ nvim Homestead.yaml</code></pre><p>First tell Vagrant that Virtualbox will be providing the virtual machine.</p><pre><code class="yaml">set provider: virtualbox</code></pre><p>Vagrant needs to setup the ssh keys between the host and the guest so that you can connect via ssh. Enter the path to the one created earlier.</p><pre><code class="yaml">authorize: ~/.ssh/homestead/id_rsa.pub keys: -- ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: -- map: ~/projects/testsite -to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: -- map: testsite.local -to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: -- testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: -- mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead + - ~/.ssh/homestead/id_rsa</code></pre><p>Share the project folder with the virtual machine. This setting will make the directory /home/vagrant/projects/testsite available in the virtual machine. The contents of this directory will be shared with the host machine directory ~/projects/testsite.</p><pre><code class="yaml">folders: + - map: ~/projects/testsite + to: /home/vagrant/projects/testsite</code></pre><p>Setup Homestead so that it can serve the application through the 'domain' testsite.local. Note how this matches the name added to /etc/hosts earlier.</p><pre><code class="yaml">sites: + - map: testsite.local + to: /home/vagrant/projects/testsite/public</code></pre><p>Have Homestead create a database for our application.</p><pre><code class="yaml">databases: + - testsite</code></pre><p>Since I'm using a database ensure that a database server is installed on the virtual machine.</p><pre><code class="yaml">features: + - mariadb: true</code></pre><h2>Launching Homestead</h2><p>Homestead is started with the vagrant up command. It may take a while for Homestead to launch if this is the first time running this command as Vagrant has to first download the actual virtual machine file.</p><pre><code class="shell">$ cd ~/.local/share/homestead $ vagrant up</code></pre><p>Once the machine is booted I can browse to http://testsite.local/ to see the simple site that is now served by Homestead.</p><h2>Installing a Laravel Site</h2><p>Now that Homestead is installed and serving a simple site its time to move onto installing the first Laravel application. Since Homestead provides all the tools required to do this the first thing to do is connect to the virtual machine.</p><pre><code class="shell">$ cd ~/.local/share/homestead -$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +$ vagrant ssh</code></pre><p>Once connected to the virtual machine navigate to the project folder of the site. Remember that this is the folder that is also been shared with the host machine.</p><pre><code class="shell">$ cd ~/projects/testsite</code></pre><p>Clear the contents of this folder otherwise composer will complain about a non-empty directory.</p><pre><code class="shell ">$ rm -rf public</code></pre><p>Use composer to install a Laravel project.</p><pre><code class="shell">$ composer create-project laravel/laravel .</code></pre><h2>Setting Up The Application Database</h2><p>Once Larvel is installed a database needs to be created for the application. Connect to the database server with the mysql command.</p><pre><code class="shell">$ mysql -uhomestead -psecret</code></pre><p>Check that the application's database was created when the virtual machine was first booted.</p><pre><code class="mysql">SHOW DATABASES; +--------------------+ | Database | @@ -63,8 +63,8 @@ DB_PORT=3306 DB_DATABASE=testsite DB_USERNAME=testsite DB_PASSWORD=testsite</pre><p>Now when you browse to http://testsite.local you will see the Laravel welcome page.</p><h2>Simplified SSH</h2><p>I prefer to just use the host system's ssh command to connect to Homestead as it cuts out having to navigate to the Homestead directory and running vagrant ssh.</p><p>To simplify ssh I first add a hostname for the virtual machine to the file /etc/hosts/</p><pre><code class="shell">192.168.10.10 homestead</code></pre><p>I then edit ~/.ssh/config and add the below configuration. This tells ssh to automatically use the keys and username specified when connecting to the virtual machine.</p><pre><code class="ssh">Host homestead -IdentityFile ~/.ssh/homestead/id_rsa -User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + IdentityFile ~/.ssh/homestead/id_rsa + User vagrant</code></pre><p>From now on I can simply do ssh homestead from any directory to connect to the Homestead virtual machine.</p><h3>Links</h3><a href="https://laravel.com/docs/8.x/homestead/">Laravel Homestead</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry><entry> <title type="text">SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</title> <id>https://davidtsadler.com/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/index.html</id> @@ -76,21 +76,21 @@ User vagrant</code></pre><p>From now on I can simply do ssh homestead from any d Illuminate\Database\QueryException -SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') - -at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 -674▕ // If an exception occurs when attempting to run a query, we'll format the error -675▕ // message to include the bindings with SQL, which will make this exception a -676▕ // lot more helpful to the developer instead of just the database's errors. -677▕ catch (Exception $e) { -678▕ throw new QueryException( -679▕ $query, $this->prepareBindings($bindings), $e -680▕ ); -681▕ } -682▕ - -+33 vendor frames -34 artisan:37 -Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution (SQL: select * from information_schema.tables where table_schema = testsite and table_name = migrations and table_type = 'BASE TABLE') + + at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678 + 674▕ // If an exception occurs when attempting to run a query, we'll format the error + 675▕ // message to include the bindings with SQL, which will make this exception a + 676▕ // lot more helpful to the developer instead of just the database's errors. + 677▕ catch (Exception $e) { + 678▕ throw new QueryException( + 679▕ $query, $this->prepareBindings($bindings), $e + 680▕ ); + 681▕ } + 682▕ + + +33 vendor frames + 34 artisan:37 + Illuminate\Foundation\Console\Kernel::handle()</code></pre><p>The cause of this issue is due to a change introduced to the .env.example file. This changed the environment variable DB_HOST from 127.0.0.1 to mysql. The reason for this change is to support [Laravel Sail](https://laravel.com/docs/8.x/sail) which is a Docker development environment for Laravel.</p><p>The change means your Laravel application will try and connect to a database server with the hostname of mysql. Unless this exists then the application can't connect.</p><p>To resolve the issue just change the value back to 127.0.0.1</p><pre><code class="shell">DB_HOST=127.0.0.1</code></pre><h3>Links</h3><a href="https://github.com/laravel/laravel/commit/a895748980b3e055ffcb68b6bc1c2e5fad6ecb08#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8cL11">Commit that changed .env.example.</a><a href="https://laravel.com/docs/8.x/sail/">Laravel Sail</a><a href="/posts/larvel/">Laravel - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/laravel/index.html b/www/posts/laravel/index.html index a5295c4..d410bf2 100644 --- a/www/posts/laravel/index.html +++ b/www/posts/laravel/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Laravel</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Laravel</h1><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Laravel</h1><a href="/posts/laravel/2020-12-21/installing-laravel-homestead-in-arch-linux/">2020-12-21 - Installing Laravel Homestead in Arch Linux</a><a href="/posts/laravel/2020-12-14/sqlstate-hy000-2002-php-network-getaddresses-getaddrinfo-failed/">2020-12-14 - SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.html b/www/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.html index 88f1a78..a666034 100644 --- a/www/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.html +++ b/www/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Sudo: sorry, you must have a tty to run sudo</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -19,6 +22,8 @@ </head> <body> <section><h1>Sudo: sorry, you must have a tty to run sudo</h1><blockquote>Mon 13th July 2020 By David T. Sadler.</blockquote><p>I have found Deployer to be a great tool for deploying PHP applications. However when first setting out to use it I soon came across the error message 'sudo: sorry, you must have a tty to run sudo'. After some investigation I found that the error was triggered when Deployer was running commands via sudo. For those that don't know Deployer works by executing commands on your servers via ssh and depending on your server's configuration there could be issues when sudo is one of those commands.</p><h2>What is meant by 'sudo: sorry, you must have a tty to run sudo'?</h2><p>When sudo is executed the file /etc/sudoers is read to determine which users or groups can use sudo and what commands they can run. It actually does a bit more than that and you should read the manual for more information.</p><p>If you examine the sudoers file you will find that it contains the setting Defaults requiretty. This means that sudo can only be ran from a real tty. In other words if a user wants to run sudo they must have logged into a terminal before hand. This is normally a security feature so that sudo can't be ran from things such as cron jobs. However, it also means that you will have issues when running sudo from another machine via ssh as you also won't be logged into an actual terminal.</p><h2>How to resolve the issue?</h2><p>If you're happy to change the setting for all users simply use visudo to edit /etc/sudoers and change Defaults requiretty to Defaults !requiretty. Alternatively you can remove the tty requirement for a single user. In fact that is what I do when using Deployer. Since it connects to the server using a user called deployer I add the below configuration with visudo.</p><pre><code class="shell">Defaults:deployer !requiretty -deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/linux/atom.xml b/www/posts/linux/atom.xml index e56ccbb..399ec15 100644 --- a/www/posts/linux/atom.xml +++ b/www/posts/linux/atom.xml @@ -13,6 +13,6 @@ <published>2020-07-13T12:00:00Z</published> <updated>2020-07-13T12:00:00Z</updated> <content>![CDATA[<h1>Sudo: sorry, you must have a tty to run sudo</h1><blockquote>Mon 13th July 2020 By David T. Sadler.</blockquote><p>I have found Deployer to be a great tool for deploying PHP applications. However when first setting out to use it I soon came across the error message 'sudo: sorry, you must have a tty to run sudo'. After some investigation I found that the error was triggered when Deployer was running commands via sudo. For those that don't know Deployer works by executing commands on your servers via ssh and depending on your server's configuration there could be issues when sudo is one of those commands.</p><h2>What is meant by 'sudo: sorry, you must have a tty to run sudo'?</h2><p>When sudo is executed the file /etc/sudoers is read to determine which users or groups can use sudo and what commands they can run. It actually does a bit more than that and you should read the manual for more information.</p><p>If you examine the sudoers file you will find that it contains the setting Defaults requiretty. This means that sudo can only be ran from a real tty. In other words if a user wants to run sudo they must have logged into a terminal before hand. This is normally a security feature so that sudo can't be ran from things such as cron jobs. However, it also means that you will have issues when running sudo from another machine via ssh as you also won't be logged into an actual terminal.</p><h2>How to resolve the issue?</h2><p>If you're happy to change the setting for all users simply use visudo to edit /etc/sudoers and change Defaults requiretty to Defaults !requiretty. Alternatively you can remove the tty requirement for a single user. In fact that is what I do when using Deployer. Since it connects to the server using a user called deployer I add the below configuration with visudo.</p><pre><code class="shell">Defaults:deployer !requiretty -deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +deployer ALL=(ALL) NOPASSWD:/usr/bin/chown, /usr/bin/tee, /usr/sbin/apachectl</code></pre><p>This configuration allows the deployer user to execute sudo when not logged into a real terminal and additionally not prompt for a password when executing chown, tee, and apachectl.</p><h2>Using Pseudo-tty</h2><p>An alternative is to use the pseudo-tty option when connecting via ssh.</p><pre><code class="shell">$ ssh -t user@example.com sudo apachectl restart</code></pre><h3>Links</h3><a href="https://deployer.org/">Deployer - A Deployment Tool for PHP.</a><a href="https://www.sudo.ws/man/sudoers.man.html">Sudo Manual.</a><a href="/posts/linux/">Linux - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/linux/index.html b/www/posts/linux/index.html index 42229b7..9f62aed 100644 --- a/www/posts/linux/index.html +++ b/www/posts/linux/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Linux</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Linux</h1><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Linux</h1><a href="/posts/linux/2020-07-13/sudo-sorry-you-must-have-a-tty-to-run-sudo/">2020-07-13 - Sudo: sorry, you must have a tty to run sudo</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.html b/www/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.html index b60c238..acbe24a 100644 --- a/www/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.html +++ b/www/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Creating an Ebook With Markdown</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -21,12 +24,12 @@ <section><h1>Creating an Ebook With Markdown</h1><blockquote>Mon 30th March 2020 By David T. Sadler.</blockquote><p>Pandoc is a great tool for converting a file in one markup format into another. This means we can use it to convert a file written in Markdown into an EPUB file that is supported by many e-readers.</p><p>Lets start by writting a very simple markdown file called example_ebook.md.</p><pre><code class="markdown">--- title: - type: main -text: Example Ebook + text: Example Ebook - type: subtitle -text: An Ebook created from a Markdown file + text: An Ebook created from a Markdown file creator: - role: author -text: David Sadler + text: David Sadler publisher: Published by myself --- @@ -54,6 +57,8 @@ This is the second paragraph of chapter 2. This is the first paragraph of chapter 3. -This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/markdown/atom.xml b/www/posts/markdown/atom.xml index 280c723..36b8547 100644 --- a/www/posts/markdown/atom.xml +++ b/www/posts/markdown/atom.xml @@ -15,12 +15,12 @@ <content>![CDATA[<h1>Creating an Ebook With Markdown</h1><blockquote>Mon 30th March 2020 By David T. Sadler.</blockquote><p>Pandoc is a great tool for converting a file in one markup format into another. This means we can use it to convert a file written in Markdown into an EPUB file that is supported by many e-readers.</p><p>Lets start by writting a very simple markdown file called example_ebook.md.</p><pre><code class="markdown">--- title: - type: main -text: Example Ebook + text: Example Ebook - type: subtitle -text: An Ebook created from a Markdown file + text: An Ebook created from a Markdown file creator: - role: author -text: David Sadler + text: David Sadler publisher: Published by myself --- @@ -48,6 +48,6 @@ This is the second paragraph of chapter 2. This is the first paragraph of chapter 3. -This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +This is the second paragraph of chapter 3.</code></pre><p>Note that the file begins with a YAML metadata block that starts and ends with three hyphens (---). This allows you to specify EPUB metadata such as the title and author.</p><p>Converting this to EPUB is done by running pandoc.</p><pre><code class="shell">$ pandoc example_ebook.md -t epub3 --toc -o example_ebook.epub</code></pre><p>There are several options that need to be passed to pandoc.</p><ul><li>example_ebook.md - This argument is the file that you are converting.</li><li>-t epub3 - Set the output format to be EPUB v3 book.</li><li>--toc - Include a table of contents in the output document. This will be derived from the H1 headers in the markdown.</li><li>-o example_ebook.epub - Tell pandoc to output the conversion to the named file instead of stdout.</li></ul><p>You can now copy the file example_ebook.epub to any device that supports the format or use one of the many software readers such as Calibre. However, if you wish to read this on a Kindle device you will need to convert it to the Mobi format.</p><p>Amazon provides a command line tool called KindleGen that can convert our EPUB file into the Mobi format. After downloading the tool just run it as shown below.</p><pre><code class="shell">$ kindlegen example_ebook.epub</code></pre><p>This will create a file called example_ebook.mobi that you can copy to your Kindle to read.</p><h3>Links</h3><a href="https://pandoc.org/">Pandoc.</a><a href="https://en.wikipedia.org/wiki/Markdown/">Markdown.</a><a href="https://en.wikipedia.org/wiki/EPUB/">EPUB.</a><a href="https://pandoc.org/MANUAL.html#extension-yaml_metadata_block">YAML metadata block.</a><a href="https://pandoc.org/MANUAL.html#epub-metadata">EPUB metadata.</a><a href="https://www.w3.org/community/epub3/">EPUB v3 book.</a><a href="https://calibre-ebook.com/">Calibre Application.</a><a href="https://en.wikipedia.org/wiki/Comparison_of_e-book_formats#Mobipocket">Mobi Format.</a><a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">KindleGen Application.</a><a href="/posts/markdown/">Markdown - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/markdown/index.html b/www/posts/markdown/index.html index c9ac93b..413df76 100644 --- a/www/posts/markdown/index.html +++ b/www/posts/markdown/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Markdown</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Markdown</h1><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Markdown</h1><a href="/posts/markdown/2020-03-30/creating-an-ebook-with-markdown/">2020-03-30 - Creating an Ebook With Markdown</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.html b/www/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.html index 1a128ae..903212d 100644 --- a/www/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.html +++ b/www/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Publishing Jigsaw Posts With Netlify Build Hooks</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/netlify/atom.xml b/www/posts/netlify/atom.xml index b7776ce..9684a4c 100644 --- a/www/posts/netlify/atom.xml +++ b/www/posts/netlify/atom.xml @@ -12,6 +12,6 @@ <author><name>David T. Sadler.</name></author> <published>2020-06-08T12:00:00Z</published> <updated>2020-06-08T12:00:00Z</updated> - <content>![CDATA[<h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + <content>![CDATA[<h1>Publishing Jigsaw Posts With Netlify Build Hooks</h1><blockquote>Mon 8th June 2020 By David T. Sadler.</blockquote><p>A previous post talks about how I use Jigsaw's filtering to schedule future posts. However because Jigsaw is a static site builder I have to run Jigsaw in order to generate the HTML for the site. So if for example I have scheduled three posts to be published over three consecutive weeks I would need to build and deploy the site on each of the scheduled dates in order for the posts to be published. This kind of defeats the point of scheduling posts. So what I needed was some way to automatically trigger new builds of the site and since I use Netlify to host the site I can make use of their build hooks to do this.</p><p>For those that don't know build hooks are URLs you can use to trigger new builds and deployments in Netlify. By making a HTTP POST request to a URL, Netlify will pull down the latest master branch of your site, build it, and then deploy it.</p><p>Creating the build hook was simple. After logging into my Netlify account I went to Settings > Build & deploy > Continuous deployment > Build hooks.</p><p>From there I clicked Add build hook where I could then provide a name and select which GitHub branch would be used to build the site.</p><p>Clicking Save creates the build hook and you are given a unique URL that can be used to trigger it.</p><p>What is very handy about Netlify is that they provide you with an example of calling the URL with cURL. With this I simply setup a cron on a server that requests this URL every Monday at 8am.</p><pre><code class="shell">8 * * 1 curl -X POST -d {} https://api.netlify.com/build_hooks/111111111111111111111111</code></pre><p>The result is that every week posts that have been scheduled for that day will now be published when the site is built and deployed by the build hook.</p><h3>Links</h3><a href="/posts/jigsaw/2020-06-01/scheduling-posts-in-jigsaw/">2020-06-01 - Scheduling Posts in Jigsaw</a><a href="https://jigsaw.tighten.co/">Jigsaw - Static Site Generator for PHP Developers.</a><a href="https://www.netlify.com/">Netlify - Serverless Platform for Static Websites.</a><a href="https://docs.netlify.com/configure-builds/build-hooks/">Build Hooks in Netlify.</a><a href="/posts/netlify/">Netlify - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/netlify/index.html b/www/posts/netlify/index.html index 27add2d..8ede82c 100644 --- a/www/posts/netlify/index.html +++ b/www/posts/netlify/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Netlify</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Netlify</h1><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Netlify</h1><a href="/posts/netlify/2020-06-08/publishing-jigsaw-posts-with-netlify-build-hooks/">2020-06-08 - Publishing Jigsaw Posts With Netlify Build Hooks</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.html b/www/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.html index 203a309..823371e 100644 --- a/www/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.html +++ b/www/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Accessing Nextcloud With WebDAV on Arch</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><h3>Links</h3><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/nextcloud/atom.xml b/www/posts/nextcloud/atom.xml index 0c30a53..d0548e0 100644 --- a/www/posts/nextcloud/atom.xml +++ b/www/posts/nextcloud/atom.xml @@ -12,6 +12,6 @@ <author><name>David T. Sadler.</name></author> <published>2021-02-15T12:00:00Z</published> <updated>2021-02-15T12:00:00Z</updated> - <content>![CDATA[<h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> + <content>![CDATA[<h1>Accessing Nextcloud With WebDAV on Arch</h1><blockquote>Mon 15th February 2021 By David T. Sadler.</blockquote><p>I have a Nextcloud instance and I want to mount it as a directory on my local machine. Since Nextcloud cloud supports the WebDAV protocol its possible to do this by installing davfs2 which can mount a WebDAV resource.</p><p>The first thing I had to do was install davfs2.</p><pre><code class="shell">$ sudo pacman -S davfs2</code></pre><p>Next I created the directory where Nextcloud would be mounted.</p><pre><code class="shell">$ mkdir -p .local/share/nextcloud</code></pre><p>I then needed to tell Arch how to mount Nextcloud by adding the below line to the /etc/fstab file.</p><pre><code class="shell">https://my-nextcloud-server.com/path /home/david/.local/share/nextcloud davfs rw,user,uid=david,noauto 0 0</code></pre><p>Since access to Nextcloud is controlled by a username and password these where added to the ~/.davfs2 file.</p><pre><code class="shell">https://my-nextcloud-server.com/path username password</code></pre><p>I made sure this file had the correct permissions to ensure security.</p><pre><code class="shell">$ chmod 600 ~/.davfs2/secrets</code></pre><p>Now I can mount Nextcloud and access my files just like any others.</p><pre><code class="shell">$ mount .local/share/nextcloud</code></pre><h3>Links</h3><a href="/posts/nextcloud">Nextcloud - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/nextcloud/index.html b/www/posts/nextcloud/index.html index f127105..4a2f2ca 100644 --- a/www/posts/nextcloud/index.html +++ b/www/posts/nextcloud/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About Nextcloud</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About Nextcloud</h1><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About Nextcloud</h1><a href="/posts/nextcloud/2021-02-15/accessing-nextcloud-with-webdav-on-arch/">2021-02-15 - Accessing Nextcloud With WebDAV on Arch</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/php/2021-01-18/installing-php-8-for-windows-10/index.html b/www/posts/php/2021-01-18/installing-php-8-for-windows-10/index.html index cc7d147..457a7af 100644 --- a/www/posts/php/2021-01-18/installing-php-8-for-windows-10/index.html +++ b/www/posts/php/2021-01-18/installing-php-8-for-windows-10/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Installing PHP 8 for Windows 10</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,10 +21,12 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system.</p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable.</p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v + <section><h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system. </p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable. </p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v PHP 8.0.1 (cli) (built: Jan 5 2021 23:43:33) ( NTS Visual C++ 2019 x64 ) Copyright (c) The PHP Group -Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> +Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> diff --git a/www/posts/php/atom.xml b/www/posts/php/atom.xml index d64aca0..fcb288c 100644 --- a/www/posts/php/atom.xml +++ b/www/posts/php/atom.xml @@ -12,10 +12,10 @@ <author><name>David T. Sadler.</name></author> <published>2021-01-18T12:00:00Z</published> <updated>2021-01-18T12:00:00Z</updated> - <content>![CDATA[<h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system.</p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable.</p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v + <content>![CDATA[<h1>Installing PHP 8 for Windows 10</h1><blockquote>Mon 18th January 2021 By David T. Sadler.</blockquote><h2>Getting Started</h2><p>The PHP For Windows site provides pre-built Windows binaries for you to download. Which version you download depends upon two things.</p><p>1. Is your system a 64 or 32 bit machine.</p><p>2. Are you planning to use IIS or Apache as the web server.</p><p>For 64 bit systems the x64 file should be downloaded. If you plan to use IIS then get the Non Thread Safe (NTS) version otherwise use the Thread Safe (TS) version.</p><p>The PHP 8 binaries require to have the Visual C++ Redistributable for Visual Studio 2015-2019 be installed as well.</p><h2>Download and Installation</h2><p>1. Download the Visual C++ Redistributable for Visual Studio 2015-2019 executable and install it.</p><p>2. Download the appropriate PHP 8 Zip archive for your Windows system. </p><p>3. Extract the Zip archive into a folder called php in your user folder. This should result in a folder at C:\Users\[username]\php.</p><h2>Configuring Windows</h2><p>In order to run the PHP executable from the command line the path to where the Zip archive was extracted to needs to be added to the Windows Path environment variable. </p><p>1. Right click on the start menu and select System.</p><p>2. Type Control Panel into the search field and select the Control Panel option when it appears.</p><p>3. Click System and Security and then the System option.</p><p>4. Click Advanced system settings from the left side menu to bring up a dialog box.</p><p>5. Select the Advanced tab and then click Environment Variables.</p><p>6. Select the Path option from the User variables list and click Edit.</p><p>7. Click New and enter the path to where you extracted the Zip archive. This should be C:\Users\[username]\php. You can also click Browse instead and navigate to the folder.</p><p>8. Click OK to close the Edit environment variable dialog.</p><p>9. Click OK again to close the Environment Variables dialog.</p><p>10. Click OK for a third time to close the System Properties dialog.</p><h2>Checking Installation</h2><p>Open up either PowerShell or the Command Prompt and enter php -v to verify that PHP was installed correctly. You should see output similar to that shown below.</p><pre><code class="shell">C:\Users\dev>php -v PHP 8.0.1 (cli) (built: Jan 5 2021 23:43:33) ( NTS Visual C++ 2019 x64 ) Copyright (c) The PHP Group -Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> +Zend Engine v4.0.1, Copyright (c) Zend Technologies</code></pre><h3>Links</h3><a href="https://windows.php.net/">PHP For Windows.</a><a href="/posts/php">PHP - Read More Posts.</a><p>I don't have comments as I don't want to manage them. You can however contact me at the below address if you want to.</p><a href="mailto:david@davidtsadler.com">Email david@davidtsadler.com</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a>]]</content> </entry> </feed>
\ No newline at end of file diff --git a/www/posts/php/index.html b/www/posts/php/index.html index bd735c9..f94951e 100644 --- a/www/posts/php/index.html +++ b/www/posts/php/index.html @@ -5,6 +5,9 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Home of David T. Sadler - All Posts About PHP</title> <link rel="shortcut icon" href="/images/favicon.png"> + <link rel="stylesheet" href="/css/modern-normalize.min.css"> + <link rel="stylesheet" href="/css/highlight.min.css"> + <link rel="stylesheet" href="/css/railscasts.css"> <link rel="stylesheet" href="/css/site.css"> <link href="/posts/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts"/> <link href="/posts/arch/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About Arch"/> @@ -18,6 +21,8 @@ <link href="/posts/php/atom.xml" type="application/atom+xml" rel="alternate" title="The Home of David T. Sadler - All Posts About PHP"/> </head> <body> - <section><h1>The Home of David T. Sadler - All Posts About PHP</h1><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <section><h1>The Home of David T. Sadler - All Posts About PHP</h1><a href="/posts/php/2021-01-18/installing-php-8-for-windows-10/">2021-01-18 - Installing PHP 8 for Windows 10</a><h3>License</h3><a href="https://creativecommons.org/licenses/by-sa/4.0/">The contents of this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</a><p>Copyright © 2021 David T. Sadler.</p><a href="/">Return to Homepage.</a></section> + <script src="/js/highlight.min.js"></script> + <script src="/js/site.js"></script> </body> </html> |
