Commit 3d70db647d

3d70db647d43bb449ada73b56f98248a560befee

parent: 972b15ecc9

Unsigned

cmc <hello@cleberg.net> · 2024-09-02 03:03 UTC

format 2024 blog posts

Layout: unified · split

content/blog/2024-01-08-dont-say-hello.org +16 −16
@@ -3,25 +3,25 @@
3#+description: 3#+description:
4#+slug: dont-say-hello 4#+slug: dont-say-hello
5 5
6I recently came back from a winter break and have started working 6I recently came back from a winter break and have started working again... only
7again... only to immediately run into the dilemma of people sending me 7to immediately run into the dilemma of people sending me cliffhanger messages
8cliffhanger messages again. 8again.
9 9
10* No Hello 10* No Hello
11 11
12A year or two ago, I discovered [[https://nohello.net/en/][no hello]] 12A year or two ago, I discovered [[https://nohello.net/en/][no hello]] and have thought about it often since
13and have thought about it often since then. I've even sent it to a few 13then. I've even sent it to a few people since then (who wouldn't take offense to
14people since then (who wouldn't take offense to it). 14it).
15 15
16I work in a fast-paced environment where efficiency is extremely 16I work in a fast-paced environment where efficiency is extremely valuable.
17valuable. Therefore, I have always held a deep displeasure for 17Therefore, I have always held a deep displeasure for conversations where people
18conversations where people start with "Hello" and then. 18start with "Hello" and then.
19 19
20I searched back through my work messages and found that I received ~50 20I searched back through my work messages and found that I received ~50 messages
21messages from ~10 people last year from people that contained "hi", 21from ~10 people last year from people that contained "hi", "hey", or "hello" and
22"hey", or "hello" and did not contain any indication of the purpose of 22did not contain any indication of the purpose of the conversation. I also
23the conversation. I also noticed that a few of the users were 23noticed that a few of the users were responsible for the large majority of the
24responsible for the large majority of the cliffhangers. 24cliffhangers.
25 25
26There's no real point to this post, just a desparate request for people 26There's no real point to this post, just a desparate request for people to
27to please stop doing this. 27please stop doing this.
content/blog/2024-01-09-macos-customization.org +52 −68
@@ -3,73 +3,65 @@
3#+description: 3#+description:
4#+slug: macos-customization 4#+slug: macos-customization
5 5
6I have been using macOS more than Linux lately, so I wrote this post to 6I have been using macOS more than Linux lately, so I wrote this post to describe
7describe some simple options to customize macOS beyond the normal 7some simple options to customize macOS beyond the normal built-in settings menu.
8built-in settings menu.
9 8
10While not all-encompassing, the options below should be a good start for 9While not all-encompassing, the options below should be a good start for anyone
11anyone looking to dive down the rabbit hole. 10looking to dive down the rabbit hole.
12 11
13* Basics 12* Basics
14 13
15** Package Management 14** Package Management
16 15
17To install a lot of software on macOS, you will need 16To install a lot of software on macOS, you will need [[https://brew.sh/][Homebrew]]. You can use their
18[[https://brew.sh/][Homebrew]]. You can use their installation script to 17installation script to get started. Simply open the =Terminal= application and
19get started. Simply open the =Terminal= application and paste the 18paste the following snippet:
20following snippet:
21 19
22#+begin_src sh 20#+begin_src sh
23/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 21/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
24#+end_src 22#+end_src
25 23
26This will allow you to easily install and manage applications and other 24This will allow you to easily install and manage applications and other software
27software easily through the =brew= command. 25easily through the =brew= command.
28 26
29** Terminal 27** Terminal
30 28
31If you're serious about customizing your macOS system, I highly 29If you're serious about customizing your macOS system, I highly recommend
32recommend installing a terminal emulator that you like and if you're not 30installing a terminal emulator that you like and if you're not comfortable on
33comfortable on the command line yet, start learning. A lot of 31the command line yet, start learning. A lot of customization options require you
34customization options require you to edit hidden files, which is easiest 32to edit hidden files, which is easiest in a terminal.
35in a terminal.
36 33
37There are options like iTerm2, Kitty, Alacritty, Hyper, Warp, or the 34There are options like iTerm2, Kitty, Alacritty, Hyper, Warp, or the built-in
38built-in Terminal app. 35Terminal app.
39 36
40I use [[https://iterm2.com/][iTerm2]], which can be installed with 37I use [[https://iterm2.com/][iTerm2]], which can be installed with Homebrew:
41Homebrew:
42 38
43#+begin_src sh 39#+begin_src sh
44brew install iterm2 40brew install iterm2
45#+end_src 41#+end_src
46 42
47To install color schemes, such as the Dracula scheme, you visit 43To install color schemes, such as the Dracula scheme, you visit [[https://iterm2colorschemes.com/][iTerm Themes]] and
48[[https://iterm2colorschemes.com/][iTerm Themes]] and follow their 44follow their installation instructions to install any of the themes.
49installation instructions to install any of the themes.
50 45
51* Desktop 46* Desktop
52 47
53** Window Management 48** Window Management
54 49
55[[https://github.com/koekeishiya/yabai][yabai]] is a tiling window 50[[https://github.com/koekeishiya/yabai][yabai]] is a tiling window manager for macOS. While other window managers exist, I
56manager for macOS. While other window managers exist, I found that most 51found that most of them struggled to create logical layouts and to allow me to
57of them struggled to create logical layouts and to allow me to easily 52easily move windows around the screen.
58move windows around the screen.
59 53
60Some advanced settings for yabai are only available if partially disable 54Some advanced settings for yabai are only available if partially disable System
61System Integrity Protection (SIP). However, I chose not to do this and 55Integrity Protection (SIP). However, I chose not to do this and it hasn't
62it hasn't affected my basic usage of yabai at all. 56affected my basic usage of yabai at all.
63 57
64Refer to the 58Refer to the [[https://github.com/koekeishiya/yabai/wiki/Installing-yabai-(latest-release)][yabai wiki]] for installation instructions. You will need to ensure
65[[https://github.com/koekeishiya/yabai/wiki/Installing-yabai-(latest-release)][yabai 59that yabai is allowed to access the accessibility and screen recording APIs.
66wiki]] for installation instructions. You will need to ensure that yabai
67is allowed to access the accessibility and screen recording APIs.
68 60
69** Keyboard Shortcuts 61** Keyboard Shortcuts
70 62
71[[https://github.com/koekeishiya/skhd][skhd]] is a simple hotkey daemon 63[[https://github.com/koekeishiya/skhd][skhd]] is a simple hotkey daemon that allows you to define hotkeys in a file for
72that allows you to define hotkeys in a file for usage on your system. 64usage on your system.
73 65
74Installation is simple: 66Installation is simple:
75 67
@@ -78,8 +70,8 @@ brew install koekeishiya/formulae/skhd
78skhd --start-service 70skhd --start-service
79#+end_src 71#+end_src
80 72
81After installation, be sure to allow =skhd= access to the accessibility 73After installation, be sure to allow =skhd= access to the accessibility API in
82API in the macOS privacy settings. 74the macOS privacy settings.
83 75
84You can configure your hotkeys in the =~/.config/skhd/skhdrc= file: 76You can configure your hotkeys in the =~/.config/skhd/skhdrc= file:
85 77
@@ -99,53 +91,45 @@ cmd + shift - return : /Applications/LibreWolf.app/Contents/MacOS/librewolf
99 91
100** Widgets 92** Widgets
101 93
102[[https://github.com/felixhageloh/uebersicht/][uebersicht]] is a handy 94[[https://github.com/felixhageloh/uebersicht/][uebersicht]] is a handy desktop-based widget tool with a plethora of
103desktop-based widget tool with a plethora of community-made widgets 95community-made widgets available in the [[https://tracesof.net/uebersicht-widgets/][widgets gallery]]. You can also write your
104available in the [[https://tracesof.net/uebersicht-widgets/][widgets 96own widgets with this tool.
105gallery]]. You can also write your own widgets with this tool.
106 97
107To install, simply download the latest release from the 98To install, simply download the latest release from the [[https://tracesof.net/uebersicht/][uebersicht website]] and
108[[https://tracesof.net/uebersicht/][uebersicht website]] and copy it to 99copy it to the Applications folder.
109the Applications folder.
110 100
111See below for an example of the 101See below for an example of the [[https://tracesof.net/uebersicht-widgets/#Mond][Mond]] widget in action.
112[[https://tracesof.net/uebersicht-widgets/#Mond][Mond]] widget in
113action.
114 102
115** Status Bar 103** Status Bar
116 104
117[[https://github.com/FelixKratz/SketchyBar][SketchyBar]] is a 105[[https://github.com/FelixKratz/SketchyBar][SketchyBar]] is a customizable replacement for the macOS status or menu bar.
118customizable replacement for the macOS status or menu bar.
119 106
120You can browse a discussion where various users shared their 107You can browse a discussion where various users shared their [[https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top][configurations]] for
121[[https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top][configurations]] 108inspiration or to copy their dotfiles.
122for inspiration or to copy their dotfiles.
123 109
124** Dock 110** Dock
125 111
126The easiest way to customize the dock is to install 112The easiest way to customize the dock is to install [[https://ubarapp.com/][uBar]], which uses a
127[[https://ubarapp.com/][uBar]], which uses a Windows-like menu bar as 113Windows-like menu bar as the default style.
128the default style.
129 114
130However, the built-in macOS dock cannot be disabled and can only be set 115However, the built-in macOS dock cannot be disabled and can only be set to
131to "always hidden". This can be annoying as it will pop out any time 116"always hidden". This can be annoying as it will pop out any time your mouse
132your mouse cursor passes closely to the dock edge of the screen. Because 117cursor passes closely to the dock edge of the screen. Because of this, I simply
133of this, I simply use the built-in dock instead of customizing it with 118use the built-in dock instead of customizing it with third-party software.
134third-party software.
135 119
136Regardless, see below for the default installation style of uBar. 120Regardless, see below for the default installation style of uBar.
137 121
138** Application Icons 122** Application Icons
139 123
140You can also customize the icon of any application in macOS, which will 124You can also customize the icon of any application in macOS, which will show up
141show up in Finder, the Dock, Launchpad, search results, etc. I recommend 125in Finder, the Dock, Launchpad, search results, etc. I recommend using
142using [[https://macosicons.com/][macOSicons]] to download icons you 126[[https://macosicons.com/][macOSicons]] to download icons you want, and then apply them by following this
143want, and then apply them by following this process. 127process.
144 128
1451. Open the Finder application. 1291. Open the Finder application.
1462. Navigate to the =Applications= folder. 1302. Navigate to the =Applications= folder.
1473. Right-click an application of your choice, and select =Get Info=. 1313. Right-click an application of your choice, and select =Get Info=.
1484. Drag the image you downloaded on top of the application's icon at the 1324. Drag the image you downloaded on top of the application's icon at the top of
149 top of information window (you will see a green "plus" symbol when 133 information window (you will see a green "plus" symbol when you're hovering
150 you're hovering over it). 134 over it).
1515. Release the new icon on top of the old icon and it will update! 1355. Release the new icon on top of the old icon and it will update!
content/blog/2024-01-13-local-llm.org +43 −51
@@ -5,11 +5,10 @@
5 5
6* Requirements 6* Requirements
7 7
8I've recently started playing with large language models (LLMs), mostly 8I've recently started playing with large language models (LLMs), mostly in the
9in the popular chatbot form, as part of my job and have decided to see 9popular chatbot form, as part of my job and have decided to see if there's a
10if there's a consistent and reliable way to interact with these models 10consistent and reliable way to interact with these models on Apple devices
11on Apple devices without sacrificing privacy or requiring in-depth 11without sacrificing privacy or requiring in-depth technical setup.
12technical setup.
13 12
14My requirements for this test: 13My requirements for this test:
15 14
@@ -18,12 +17,12 @@ My requirements for this test:
18- Minimal required configuration 17- Minimal required configuration
19- Preferably pre-built, but a simple build process is acceptable 18- Preferably pre-built, but a simple build process is acceptable
20 19
21I tested a handful of apps and have summarized my favorite (so far) for 20I tested a handful of apps and have summarized my favorite (so far) for macOS
22macOS and iOS below. 21and iOS below.
23 22
24#+begin_quote 23#+begin_quote
25TL;DR - Here are the two that met my requirements and I have found the 24TL;DR - Here are the two that met my requirements and I have found the easiest
26easiest to install and use so far: 25to install and use so far:
27#+end_quote 26#+end_quote
28 27
29- macOS: [[https://ollama.ai/][Ollama]] 28- macOS: [[https://ollama.ai/][Ollama]]
@@ -31,13 +30,12 @@ easiest to install and use so far:
31 30
32* macOS 31* macOS
33 32
34[[https://ollama.ai/][Ollama]] is a simple Go application for macOS and 33[[https://ollama.ai/][Ollama]] is a simple Go application for macOS and Linux that can run various LLMs
35Linux that can run various LLMs locally. 34locally.
36 35
37For macOS, you can download the pplication on the 36For macOS, you can download the pplication on the [[https://ollama.ai/download/mac][Ollama download page]] and
38[[https://ollama.ai/download/mac][Ollama download page]] and install it 37install it by unzipping the =Ollama.app= file and moving it to the
39by unzipping the =Ollama.app= file and moving it to the =Applications= 38=Applications= folder.
40folder.
41 39
42If you prefer the command line, you can run these commands after the 40If you prefer the command line, you can run these commands after the
43download finished: 41download finished:
@@ -48,49 +46,43 @@ unzip Ollama-darwin.zip && \
48mv ~/Downloads/Ollama.app /Applications/ 46mv ~/Downloads/Ollama.app /Applications/
49#+end_src 47#+end_src
50 48
51After running the app, the app will ask you to open a terminal and run 49After running the app, the app will ask you to open a terminal and run the
52the default =llama2= model, which will open an interactive chat session 50default =llama2= model, which will open an interactive chat session in the
53in the terminal. You can startfully using the application at this point. 51terminal. You can startfully using the application at this point.
54 52
55If you don't want to use the default =llama2= model, you can download 53If you don't want to use the default =llama2= model, you can download and run
56and run additional models found on the 54additional models found on the [[https://ollama.ai/library][Models]] page.
57[[https://ollama.ai/library][Models]] page.
58 55
59To see the information for the currently-used model, you can run the 56To see the information for the currently-used model, you can run the
60=/show info= command in the chat. 57=/show info= command in the chat.
61 58
62** Community Integrations 59** Community Integrations
63 60
64I highly recommend browsing the 61I highly recommend browsing the [[https://github.com/jmorganca/ollama#community-integrations][Community Integrations]] section of the project to
65[[https://github.com/jmorganca/ollama#community-integrations][Community 62see how you would prefer to extend Ollama beyond a simple command-line
66Integrations]] section of the project to see how you would prefer to 63interface. There are options for APIs, browser UIs, advanced terminal
67extend Ollama beyond a simple command-line interface. There are options 64configurations, and more.
68for APIs, browser UIs, advanced terminal configurations, and more.
69 65
70* iOS 66* iOS
71 67
72While there are a handful of decent macOS options, it was quite 68While there are a handful of decent macOS options, it was quite difficult to
73difficult to find an iOS app that offered an open source platform 69find an iOS app that offered an open source platform without an extensive
74without an extensive configuration and building process. I found LLM 70configuration and building process. I found LLM Farm to be decent enough in
75Farm to be decent enough in quality to sit at the top of my list - 71quality to sit at the top of my list - however, it's definitely not user
76however, it's definitely not user friendly enough for me to consider 72friendly enough for me to consider using it on a daily basis.
77using it on a daily basis. 73
78 74[[https://llmfarm.site/][LLM Farm]] is available on TestFlight, so there's no manual build process
79[[https://llmfarm.site/][LLM Farm]] is available on TestFlight, so 75required. However, you can view the [[https://github.com/guinmoon/LLMFarm][LLMFarm repository]] if you wish.
80there's no manual build process required. However, you can view the 76
81[[https://github.com/guinmoon/LLMFarm][LLMFarm repository]] if you wish. 77The caveat is that you will have to manually download the model files from the
82 78links in the [[https://github.com/guinmoon/LLMFarm/blob/main/models.md][models.md]] file to your iPhone to use the app - there's currently no
83The caveat is that you will have to manually download the model files 79option in the app to reach out and grab the latest version of any supported
84from the links in the 80model.
85[[https://github.com/guinmoon/LLMFarm/blob/main/models.md][models.md]] 81
86file to your iPhone to use the app - there's currently no option in the 82Once you have a file downloaded, you simply create a new chat and select the
87app to reach out and grab the latest version of any supported model. 83downloaded model file and ensure the inference matches the requirement in the
88 84=models.md= file.
89Once you have a file downloaded, you simply create a new chat and select 85
90the downloaded model file and ensure the inference matches the 86[[https://github.com/AugustDev/enchanted][Enchanted]] is also an iOS for private AI models, but it requires a public-facing
91requirement in the =models.md= file. 87Ollama API, which did not meet my "on device requirement." Nonetheless, it's an
92 88interesting looking app and I will likely set it up to test soon.
93[[https://github.com/AugustDev/enchanted][Enchanted]] is also an iOS for
94private AI models, but it requires a public-facing Ollama API, which did
95not meet my "on device requirement." Nonetheless, it's an interesting
96looking app and I will likely set it up to test soon.
content/blog/2024-01-26-audit-dashboard.org +59 −64
@@ -3,9 +3,9 @@
3#+description: 3#+description:
4#+slug: audit-dashboard 4#+slug: audit-dashboard
5 5
6Alteryx and Power BI are powerful tools that can help turn your 6Alteryx and Power BI are powerful tools that can help turn your old-school audit
7old-school audit trackers into interactive tools that provide useful 7trackers into interactive tools that provide useful insights and potential
8insights and potential action plans. 8action plans.
9 9
10With these tools, we are going to build a dashboard that can effectively 10With these tools, we are going to build a dashboard that can effectively
11communicate project status. 11communicate project status.
@@ -15,30 +15,28 @@ communicate project status.
15This project assumes the following: 15This project assumes the following:
16 16
17- You have access to Alteryx Designer and Power BI Desktop. 17- You have access to Alteryx Designer and Power BI Desktop.
18 - If you only have Power BI Desktop, you may need to perform some 18 - If you only have Power BI Desktop, you may need to perform some analysis in
19 analysis in Power BI instead of Alteryx. 19 Power BI instead of Alteryx.
20- Your data is in a format that can be imported into Alteryx and/or 20- Your data is in a format that can be imported into Alteryx and/or Power BI.
21 Power BI.
22- You have a basic understanding of data types and visualization. 21- You have a basic understanding of data types and visualization.
23 22
24* Alteryx: Data Preparation & Analysis 23* Alteryx: Data Preparation & Analysis
25 24
26** Import Data 25** Import Data
27 26
28With Alteryx, importing data is easy with the use of the =Input Data= 27With Alteryx, importing data is easy with the use of the =Input Data= tool.
29tool. Simply drag this tool onto the canvas from the =In/Out= tab in the 28Simply drag this tool onto the canvas from the =In/Out= tab in the Ribbon to
30Ribbon to create it as a node. 29create it as a node.
31 30
32You can choose the File Format manually or simply connect to your 31You can choose the File Format manually or simply connect to your file/database
33file/database and let Alteryx determine the format for you. For this 32and let Alteryx determine the format for you. For this example, we will be
34example, we will be importing an Excel file and changing the 33importing an Excel file and changing the =Start Data Import on Line= variable to
35=Start Data Import on Line= variable to =2=. 34=2=.
36 35
37** Transform Data 36** Transform Data
38 37
39Next, let's replace null data and remove whitespace to clean up our 38Next, let's replace null data and remove whitespace to clean up our data. We can
40data. We can do this with the =Data Cleansing= tool in the =Preparation= 39do this with the =Data Cleansing= tool in the =Preparation= tab in the Ribbon.
41tab in the Ribbon.
42 40
43Ensure that the following options are enabled: 41Ensure that the following options are enabled:
44 42
@@ -48,49 +46,48 @@ Ensure that the following options are enabled:
48- Remove Unwanted Characters 46- Remove Unwanted Characters
49 - Leading and Trailing Whitespace 47 - Leading and Trailing Whitespace
50 48
51For our next step, we will transform the date fields from strings to 49For our next step, we will transform the date fields from strings to datetime
52datetime format. Add a =Datetime= tool for each field you want to 50format. Add a =Datetime= tool for each field you want to transform - in the
53transform - in the example below, I am using the tool twice for the 51example below, I am using the tool twice for the "Started On" and "Submitted On"
54"Started On" and "Submitted On" fields. 52fields.
55 53
56Now that the dates are in the correct format, let's perform a 54Now that the dates are in the correct format, let's perform a calculation based
57calculation based on those fields. Start by adding a =Filter= tool, 55on those fields. Start by adding a =Filter= tool, naming a new Output Column,
58naming a new Output Column, and pasting the formula below into it (the 56and pasting the formula below into it (the two fields used in this formula must
59two fields used in this formula must match the output of the =Datetime= 57match the output of the =Datetime= tools above):
60tools above):
61 58
62``=txt DateTimeDiff([SubmittedOn_Out],[StartedOn_Out], "days")= 59```txt
60DateTimeDiff([SubmittedOn_Out],[StartedOn_Out], "days")
61```
63 62
64** Export Data 63** Export Data
65 64
66Finalize the process by exporting the transformed data set to a new 65Finalize the process by exporting the transformed data set to a new file, for
67file, for use in the following visualization step. 66use in the following visualization step.
68 67
69* Power BI: Data Visualization 68* Power BI: Data Visualization
70 69
71** Import Data 70** Import Data
72 71
73To start, open the Power BI Desktop application. Upon first use, Power 72To start, open the Power BI Desktop application. Upon first use, Power BI will
74BI will ask if you want to open an existing dashboard or import new 73ask if you want to open an existing dashboard or import new data.
75data.
76 74
77As we are creating our first dashboard, let's import our data. In my 75As we are creating our first dashboard, let's import our data. In my example
78example below, I'm importing data from the "Tracker" sheet of the Excel 76below, I'm importing data from the "Tracker" sheet of the Excel file I'm using
79file I'm using for this project. 77for this project.
80 78
81During this process, I also imported the export from the Alteryx 79During this process, I also imported the export from the Alteryx workflow above.
82workflow above. Therefore, we have two different files available for use 80Therefore, we have two different files available for use in our dashboard.
83in our dashboard.
84 81
85** Add Visuals 82** Add Visuals
86 83
87To create the dashboard, you will need to follow the list instructions 84To create the dashboard, you will need to follow the list instructions below and
88below and format as needed. 85format as needed.
89 86
90Instructions to create the visuals above: 87Instructions to create the visuals above:
91 88
92- =Text Box=: Explain the name and purpose of the dashboard. You can 89- =Text Box=: Explain the name and purpose of the dashboard. You can also add
93 also add images and logos at the top of the dashboard. 90 images and logos at the top of the dashboard.
94- =Donut Chart=: Overall status of the project. 91- =Donut Chart=: Overall status of the project.
95 - =Legend=: Status 92 - =Legend=: Status
96 - =Values=: Count of Status 93 - =Values=: Count of Status
@@ -111,13 +108,12 @@ Instructions to create the visuals above:
111 108
112** Format the Dashboard 109** Format the Dashboard
113 110
114You can choose a theme in the View tab of the Ribbon. You can even 111You can choose a theme in the View tab of the Ribbon. You can even browse for
115browse for custom JSON files that define themes, such as ones found 112custom JSON files that define themes, such as ones found online or custom ones
116online or custom ones created by your organization. 113created by your organization.
117 114
118For each visual, you can click the =Format= button in the 115For each visual, you can click the =Format= button in the =Visualizations= side
119=Visualizations= side pane and explore the options. You can custom 116pane and explore the options. You can custom options such as:
120options such as:
121 117
122- Visual 118- Visual
123 - Legend 119 - Legend
@@ -132,22 +128,21 @@ options such as:
132 - Tooltips 128 - Tooltips
133 - Alt text 129 - Alt text
134 130
135You can always look online for inspiration when trying to decide how 131You can always look online for inspiration when trying to decide how best to
136best to organize and style your dashboard. 132organize and style your dashboard.
137 133
138* Sharing the Results 134* Sharing the Results
139 135
140Generally, you have a few different options for sharing your dashboards 136Generally, you have a few different options for sharing your dashboards with
141with others: 137others:
142 138
1431. Export the dashboard as a PDF in the file menu of Power BI. This will 1391. Export the dashboard as a PDF in the file menu of Power BI. This will export
144 export all tabs and visuals as they are set when the export button is 140 all tabs and visuals as they are set when the export button is pressed. You
145 pressed. You will lose all interactivity with this option. 141 will lose all interactivity with this option.
1462. Send the full Power BI file to those you wish to share the dashboard. 1422. Send the full Power BI file to those you wish to share the dashboard. This
147 This will retain all settings and interactivity. However, you will 143 will retain all settings and interactivity. However, you will also need to
148 also need to send the source files if they need to refresh the 144 send the source files if they need to refresh the dashboard and you will need
149 dashboard and you will need to re-send the files if you make updates. 145 to re-send the files if you make updates.
1503. Store the dashboard in a synced location, such as a shared drive or 1463. Store the dashboard in a synced location, such as a shared drive or Microsoft
151 Microsoft Teams. Depending on how a user configures their local 147 Teams. Depending on how a user configures their local Windows paths, the data
152 Windows paths, the data source paths may not be compatible for all 148 source paths may not be compatible for all users with such a setup.
153 users with such a setup.
content/blog/2024-01-27-tableau-dashboard.org +42 −47
@@ -3,14 +3,12 @@
3#+description: 3#+description:
4#+slug: tableau-dashboard 4#+slug: tableau-dashboard
5 5
6In this project, I am going to show you how to use Tableau Public for 6In this project, I am going to show you how to use Tableau Public for free to
7free to create simple dashboards. 7create simple dashboards.
8 8
9I will be creating simple visuals from an Omaha crime data set and 9I will be creating simple visuals from an Omaha crime data set and combining
10combining them to create the dashboard below. You can view this 10them to create the dashboard below. You can view this dashboard interactively
11dashboard interactively online here: 11online here: [[https://public.tableau.com/app/profile/c.c7042/viz/OmahaCrimeData2015-2023/OmahaCrimeData2015-2023#1][Omaha Crime Data (2015 - 2023)]].
12[[https://public.tableau.com/app/profile/c.c7042/viz/OmahaCrimeData2015-2023/OmahaCrimeData2015-2023#1][Omaha
13Crime Data (2015 - 2023)]].
14 12
15#+caption: Tableau Dashboard 13#+caption: Tableau Dashboard
16[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]] 14[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]]
@@ -18,18 +16,17 @@ Crime Data (2015 - 2023)]].
18* Gather the Data 16* Gather the Data
19 17
20You can download incident data from the Omaha Police Department on their 18You can download incident data from the Omaha Police Department on their
21[[https://police.cityofomaha.org/crime-information/incident-data-download][Incident 19[[https://police.cityofomaha.org/crime-information/incident-data-download][Incident Data Download]] page. They currently have files for the years 2015
22Data Download]] page. They currently have files for the years 2015
23through 2023. 20through 2023.
24 21
25Each file will be downloaded as a CSV file, approximately 3 MB - 8 MB. 22Each file will be downloaded as a CSV file, approximately 3 MB - 8 MB.
26 23
27* Clean and Transform the Data 24* Clean and Transform the Data
28 25
29I have used Python to combine the files into a single CSV file, as well 26I have used Python to combine the files into a single CSV file, as well as
30as adding a custom =datetime= column. You could do this step in any 27adding a custom =datetime= column. You could do this step in any software you
31software you prefer, but I prefer Python as its free, easy to use, and 28prefer, but I prefer Python as its free, easy to use, and has a plethora of
32has a plethora of support resources online. 29support resources online.
33 30
34Start by opening a terminal, navigating to your Downloads directory, and 31Start by opening a terminal, navigating to your Downloads directory, and
35creating a python script. 32creating a python script.
@@ -76,38 +73,37 @@ Once pasted, save and close the file. You can execute the file like so:
76python3 data_processing.py 73python3 data_processing.py
77#+end_src 74#+end_src
78 75
79After this, you should have a combined data file that contains all 76After this, you should have a combined data file that contains all incidents
80incidents between 2015 and 2023. Mine was approximately 55 MB. 77between 2015 and 2023. Mine was approximately 55 MB.
81 78
82* Tableau Public 79* Tableau Public
83 80
84[[https://public.tableau.com/][Tableau Public]] is a free-to-use web 81[[https://public.tableau.com/][Tableau Public]] is a free-to-use web application that allows you to create
85application that allows you to create visualizations by uploading data 82visualizations by uploading data sources. Note that there's no way to keep the
86sources. Note that there's no way to keep the data and visualizations 83data and visualizations private, so don't upload anything private.
87private, so don't upload anything private.
88 84
89After creating an account, you can click the =Create= > =Web Authoring= 85After creating an account, you can click the =Create= > =Web Authoring= link to
90link to create your first visualization. 86create your first visualization.
91 87
92** Upload the Data 88** Upload the Data
93 89
94Once you've opened your first project, Tableau will ask you to connect 90Once you've opened your first project, Tableau will ask you to connect to your
95to your data. For this project, click the =Upload from computer= button 91data. For this project, click the =Upload from computer= button and select the
96and select the CSV file previously combined in the step above. 92CSV file previously combined in the step above.
97 93
98Once connected, you can refresh the preview of the data with the 94Once connected, you can refresh the preview of the data with the =Refresh Data
99=Refresh Data Source= button in the toolbar. 95Source= button in the toolbar.
100 96
101If you need to edit any of the data types, column names, etc., you can 97If you need to edit any of the data types, column names, etc., you can do so
102do so now. Once complete, generate an extract so that you can start 98now. Once complete, generate an extract so that you can start creating
103creating visualizations. 99visualizations.
104 100
105** Create Visualizations 101** Create Visualizations
106 102
107To start, create a worksheet in the toolbar at the bottom of the screen. 103To start, create a worksheet in the toolbar at the bottom of the screen.
108 104
109Within this screen, select a column from the =Data= side bar on the left 105Within this screen, select a column from the =Data= side bar on the left and
110and drag it into the =Columns= or =Rows= area of the canvas. 106drag it into the =Columns= or =Rows= area of the canvas.
111 107
112See below for the map visualization. You can recreate this by adding the 108See below for the map visualization. You can recreate this by adding the
113following fields: 109following fields:
@@ -119,30 +115,29 @@ following fields:
119 - Datetime 115 - Datetime
120- =Filters=: Datetime 116- =Filters=: Datetime
121 117
122You can repeat this process for each visualization you want to create. 118You can repeat this process for each visualization you want to create. Explore
123Explore your options by dragging data fields to different areas and by 119your options by dragging data fields to different areas and by opening the field
124opening the field options to explore what operations can be performed on 120options to explore what operations can be performed on different data types
125different data types (e.g., average, count, etc.). 121(e.g., average, count, etc.).
126 122
127** Create Dashboard 123** Create Dashboard
128 124
129To create a dashboard, click the button on the toolbar at the bottom of 125To create a dashboard, click the button on the toolbar at the bottom of the
130the screen. Within the dashboard, drag each sheet from the left side bar 126screen. Within the dashboard, drag each sheet from the left side bar onto the
131onto the dashboard canvas. 127dashboard canvas.
132 128
133** Formatting 129** Formatting
134 130
135You can explore a ton of different formatting options throughout the 131You can explore a ton of different formatting options throughout the worksheets
136worksheets and dashboard. Specifically for maps, you can alter the map 132and dashboard. Specifically for maps, you can alter the map layers, background,
137layers, background, and visible features through the =Map= menu in the 133and visible features through the =Map= menu in the top file menu of the editing
138top file menu of the editing screen. 134screen.
139 135
140In the finished dashboard below, I opted for a dark mode with a map that 136In the finished dashboard below, I opted for a dark mode with a map that showed
141showed county lines and city names. 137county lines and city names.
142 138
143There's a ton of other options available to be used in a dashboard like 139There's a ton of other options available to be used in a dashboard like this,
144this, but this project shows a quick preview of what you can do in 140but this project shows a quick preview of what you can do in Tableau Public.
145Tableau Public.
146 141
147#+caption: Tableau Dashboard 142#+caption: Tableau Dashboard
148[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]] 143[[https://img.cleberg.net/blog/20240127-tableau-dashboard/dashboard.png]]
content/blog/2024-02-06-zfs.org +54 −68
@@ -6,14 +6,11 @@
6This post details the process I used to create ZFS pools, datasets, and 6This post details the process I used to create ZFS pools, datasets, and
7snapshots on Ubuntu Server. 7snapshots on Ubuntu Server.
8 8
9I found the following pages very helpful while going through this 9I found the following pages very helpful while going through this process:
10process:
11 10
12- [[https://ubuntu.com/tutorials/setup-zfs-storage-pool][Setup a ZFS 11- [[https://ubuntu.com/tutorials/setup-zfs-storage-pool][Setup a ZFS storage pool]]
13 storage pool]]
14- [[https://wiki.ubuntu.com/Kernel/Reference/ZFS][Kernel/Reference/ZFS]] 12- [[https://wiki.ubuntu.com/Kernel/Reference/ZFS][Kernel/Reference/ZFS]]
15- [[https://blog.victormendonca.com/2020/11/03/zfs-for-dummies/][ZFS for 13- [[https://blog.victormendonca.com/2020/11/03/zfs-for-dummies/][ZFS for Dummies]]
16 Dummies]]
17 14
18* Installation 15* Installation
19 16
@@ -23,8 +20,7 @@ To start, I installed the ZFS package with the following command:
23sudo apt install zfsutils-linux 20sudo apt install zfsutils-linux
24#+end_src 21#+end_src
25 22
26Once installed, you can check the version to see if it installed 23Once installed, you can check the version to see if it installed correctly.
27correctly.
28 24
29#+begin_src sh 25#+begin_src sh
30> zsf --version 26> zsf --version
@@ -37,9 +33,9 @@ zfs-kmod-2.1.5-1ubuntu6~22.04.1
37 33
38Now that ZFS is installed, we can create and configure the pool. 34Now that ZFS is installed, we can create and configure the pool.
39 35
40You have various options for configuring ZFS pools that all come 36You have various options for configuring ZFS pools that all come different pros
41different pros and cons. I suggest visiting the links at the top of this 37and cons. I suggest visiting the links at the top of this post or searching
42post or searching online for the best configuration for your use-case. 38online for the best configuration for your use-case.
43 39
44- Striped VDEVs (Raid0) 40- Striped VDEVs (Raid0)
45- Mirrored VDEVs (Raid1) 41- Mirrored VDEVs (Raid1)
@@ -49,20 +45,20 @@ post or searching online for the best configuration for your use-case.
49- RAIDz3 45- RAIDz3
50- Nested RAIDz (Raid50, Raid60) 46- Nested RAIDz (Raid50, Raid60)
51 47
52I will be using Raid10 in this guide. However, the majority of the steps 48I will be using Raid10 in this guide. However, the majority of the steps are the
53are the same regardless of your chosen pool configuration. 49same regardless of your chosen pool configuration.
54 50
55** Creating the Pool 51** Creating the Pool
56 52
57To start, let's list the disks available to use. You can use =fdisk= 53To start, let's list the disks available to use. You can use =fdisk= command to
58command to see all available disks. 54see all available disks.
59 55
60#+begin_src sh 56#+begin_src sh
61sudo fdisk -l 57sudo fdisk -l
62#+end_src 58#+end_src
63 59
64Or, if you currently have them mounted, you can use the =df= command to 60Or, if you currently have them mounted, you can use the =df= command to view
65view your disks. 61your disks.
66 62
67#+begin_src sh 63#+begin_src sh
68> sudo df -h 64> sudo df -h
@@ -77,17 +73,16 @@ Filesystem Size Used Avail Use% Mounted on
77/dev/sdf1 7.3T 28K 6.9T 1% /mnt/red-04 73/dev/sdf1 7.3T 28K 6.9T 1% /mnt/red-04
78#+end_src 74#+end_src
79 75
80If you're going to use mounted disks, make sure to umount them before 76If you're going to use mounted disks, make sure to umount them before creating
81creating the pool. 77the pool.
82 78
83#+begin_src sh 79#+begin_src sh
84sudo umount /dev/sda1 80sudo umount /dev/sda1
85sudo umount /dev/sdb1 81sudo umount /dev/sdb1
86#+end_src 82#+end_src
87 83
88Now that I've identified the disks I want to use and have them 84Now that I've identified the disks I want to use and have them unmounted, let's
89unmounted, let's create the pool. For this example, I will call it 85create the pool. For this example, I will call it =tank=.
90=tank=.
91 86
92#+begin_src sh 87#+begin_src sh
93sudo zpool create -f -m /mnt/pool tank mirror /dev/sda /dev/sdb 88sudo zpool create -f -m /mnt/pool tank mirror /dev/sda /dev/sdb
@@ -119,8 +114,8 @@ config:
119errors: No known data errors 114errors: No known data errors
120#+end_src 115#+end_src
121 116
122We can also look at the mounted filesystem to see where the pool is 117We can also look at the mounted filesystem to see where the pool is mounted and
123mounted and some quick stats. 118some quick stats.
124 119
125#+begin_src sh 120#+begin_src sh
126> df -h 121> df -h
@@ -132,28 +127,25 @@ tank 7.2T 128K 7.2T 1% /tank
132 127
133** Expanding the Pool 128** Expanding the Pool
134 129
135If you want to expand this pool, you will need to add a new VDEV to the 130If you want to expand this pool, you will need to add a new VDEV to the pool.
136pool. Since I am using 2 disks per VDEV, I will need to add a new 2-disk 131Since I am using 2 disks per VDEV, I will need to add a new 2-disk VDEV to the
137VDEV to the existing =tank= pool. 132existing =tank= pool.
138 133
139#+begin_src sh 134#+begin_src sh
140sudo zpool add tank mirror /dev/sdX /dev/sdY 135sudo zpool add tank mirror /dev/sdX /dev/sdY
141#+end_src 136#+end_src
142 137
143If you're adding disks of different sizes, you'll need to use the =-f= 138If you're adding disks of different sizes, you'll need to use the =-f= flag.
144flag. Keep in mind that the max size will be limited to the smallest 139Keep in mind that the max size will be limited to the smallest disk added.
145disk added.
146 140
147#+begin_src sh 141#+begin_src sh
148sudo zpool add -f tank mirror /dev/sdX /dev/sdY 142sudo zpool add -f tank mirror /dev/sdX /dev/sdY
149#+end_src 143#+end_src
150 144
151I added two 8TB hard drives and this process took around 10 seconds to 145I added two 8TB hard drives and this process took around 10 seconds to complete.
152complete.
153 146
154When viewing the pool again, you can see that the pool has now doubled 147When viewing the pool again, you can see that the pool has now doubled in size.
155in size. We have 14.3 TB useable space and the same space used for 148We have 14.3 TB useable space and the same space used for mirroring.
156mirroring.
157 149
158#+begin_src sh 150#+begin_src sh
159> zfs list 151> zfs list
@@ -166,8 +158,8 @@ tank/media 96K 14.3T 96K /tank/media
166 158
167*** Converting Disks 159*** Converting Disks
168 160
169Some disks, such as NTFS-formatted drives, will need to be partitioned 161Some disks, such as NTFS-formatted drives, will need to be partitioned and
170and formatted prior to being added to the pool. 162formatted prior to being added to the pool.
171 163
172Start by identifying the disks you want to format and add to the pool. 164Start by identifying the disks you want to format and add to the pool.
173 165
@@ -178,8 +170,8 @@ sudo fdisk -l | grep /dev
178I am going to format my =/dev/sdc= and =/dev/sdd= disks with the =fdisk= 170I am going to format my =/dev/sdc= and =/dev/sdd= disks with the =fdisk=
179command. 171command.
180 172
181See below for instructions on how to use =fdisk=. Here's what I did to 173See below for instructions on how to use =fdisk=. Here's what I did to create
182create basic Linux formatted disks: 174basic Linux formatted disks:
183 175
184- =g= : Create GPT partition table 176- =g= : Create GPT partition table
185- =n= : Create a new partition, hit Enter for all default options 177- =n= : Create a new partition, hit Enter for all default options
@@ -191,13 +183,12 @@ I repeated this process for both disks.
191#+begin_src sh 183#+begin_src sh
192> sudo fdisk /dev/sdc 184> sudo fdisk /dev/sdc
193 185
194Welcome to fdisk (util-linux 2.37.2). 186Welcome to fdisk (util-linux 2.37.2). Changes will remain in memory only, until
195Changes will remain in memory only, until you decide to write them. 187you decide to write them. Be careful before using the write command.
196Be careful before using the write command.
197 188
198This disk is currently in use - repartitioning is probably a bad idea. 189This disk is currently in use - repartitioning is probably a bad idea. It's
199It's recommended to umount all file systems, and swapoff all swap 190recommended to umount all file systems, and swapoff all swap partitions on this
200partitions on this disk. 191disk.
201 192
202 193
203Command (m for help): m 194Command (m for help): m
@@ -243,9 +234,9 @@ sudo zpool add tank mirror /dev/sdc /dev/sdd
243#+end_src 234#+end_src
244 235
245When we list the pool again, we can see that our size is now updated to 236When we list the pool again, we can see that our size is now updated to
246approximately 22TB. This represents my hard drives totalling 45.6TB when 237approximately 22TB. This represents my hard drives totalling 45.6TB when shown
247shown with =fdisk -l=, with a Raid10 configuration using 22TB for 238with =fdisk -l=, with a Raid10 configuration using 22TB for mirroring and 22TB
248mirroring and 22TB of useable space. 239of useable space.
249 240
250#+begin_src sh 241#+begin_src sh
251> zfs list 242> zfs list
@@ -258,17 +249,15 @@ tank/media 145GT 21.7T 96K /tank/media
258 249
259** Creating Datasets 250** Creating Datasets
260 251
261According to 252According to [[https://docs.oracle.com/cd/E18752_01/html/819-5461/ftyue.html][ZFS Terminology]], a =dataset= can refer to “clones, file systems,
262[[https://docs.oracle.com/cd/E18752_01/html/819-5461/ftyue.html][ZFS
263Terminology]], a =dataset= can refer to “clones, file systems,
264snapshots, and volumes. 253snapshots, and volumes.
265 254
266For this guide, I will use the =dataset= term to refer to file systems 255For this guide, I will use the =dataset= term to refer to file systems created
267created under a pool. 256under a pool.
268 257
269Within my =tank= pool, I am going to create some datasets to help 258Within my =tank= pool, I am going to create some datasets to help organize my
270organize my files. This will give me location to store data rather than 259files. This will give me location to store data rather than simply dumping
271simply dumping everything at the =/tank/= location. 260everything at the =/tank/= location.
272 261
273#+begin_src sh 262#+begin_src sh
274sudo zfs create tank/cloud 263sudo zfs create tank/cloud
@@ -288,9 +277,9 @@ tank/media 96K 7.14T 96K /tank/media
288 277
289** Creating Snapshots 278** Creating Snapshots
290 279
291Next, let's create our first snapshot. We can do this by calling the 280Next, let's create our first snapshot. We can do this by calling the =snapshot=
292=snapshot= command and give it an output name. I will be throwing the 281command and give it an output name. I will be throwing the current date and time
293current date and time into my example. 282into my example.
294 283
295#+begin_src sh 284#+begin_src sh
296sudo zfs snapshot tank@$(date '+%Y-%m-%d_%H-%M') 285sudo zfs snapshot tank@$(date '+%Y-%m-%d_%H-%M')
@@ -321,13 +310,10 @@ no datasets available
321 310
322* My Thoughts on ZFS So Far 311* My Thoughts on ZFS So Far
323 312
324- I sacrificed 25TB to be able to mirror my data, but I feel more 313- I sacrificed 25TB to be able to mirror my data, but I feel more comfortable
325 comfortable with the potential to save my data by quickly replacing a 314 with the potential to save my data by quickly replacing a disk if I need to.
326 disk if I need to.
327- The set-up was surprisingly easy and fast. 315- The set-up was surprisingly easy and fast.
328- Disk I/O is fast as well. I was worried that the data transfer speeds 316- Disk I/O is fast as well. I was worried that the data transfer speeds would be
329 would be slower due to the RAID configuration. 317 slower due to the RAID configuration.
330- Media streaming and transcoding has seen no noticeable drop in 318- Media streaming and transcoding has seen no noticeable drop in performance.
331 performance. 319- My only limitation really is the number of HDD bays in my server HDD cage.
332- My only limitation really is the number of HDD bays in my server HDD
333 cage.
content/blog/2024-02-13-ubuntu-emergency-mode.org +21 −22
@@ -5,12 +5,12 @@
5 5
6* The Problem 6* The Problem
7 7
8I recently [[../zfs/][migrated my hard drives to a ZFS pool]] and found 8I recently [[../zfs/][migrated my hard drives to a ZFS pool]] and found myself stuck in
9myself stuck in Ubuntu's emergency mode after the first reboot I 9Ubuntu's emergency mode after the first reboot I performed after creating the
10performed after creating the ZFS pool. 10ZFS pool.
11 11
12My server was stuck in the boot process and showed the following error 12My server was stuck in the boot process and showed the following error on the
13on the screen: 13screen:
14 14
15#+begin_src txt 15#+begin_src txt
16You are in emergency mode. 16You are in emergency mode.
@@ -19,34 +19,33 @@ After logging in, type "journalctl -xb" to view system logs,
19or ^D to try again to boot into default mode". 19or ^D to try again to boot into default mode".
20#+end_src 20#+end_src
21 21
22After rebooting the server and watching the logs scroll on a monitor, I 22After rebooting the server and watching the logs scroll on a monitor, I noticed
23noticed the root cause was related to a very long search for certain 23the root cause was related to a very long search for certain drives. I kept
24drives. I kept seeing errors like this: 24seeing errors like this:
25 25
26#+begin_src txt 26#+begin_src txt
27[ TIME ] Timed out waiting of device dev-disk-by/[disk-uuid] 27[ TIME ] Timed out waiting of device dev-disk-by/[disk-uuid]
28#+end_src 28#+end_src
29 29
30I realized that I had not removed the =/etc/fstab= references that asked 30I realized that I had not removed the =/etc/fstab= references that asked Ubuntu
31Ubuntu to mount two disks on boot, but I had recently changed those 31to mount two disks on boot, but I had recently changed those disks to be part of
32disks to be part of my ZFS pool instead. Therefore, Ubuntu was trying to 32my ZFS pool instead. Therefore, Ubuntu was trying to identify and mount a disk
33identify and mount a disk that was not available. 33that was not available.
34 34
35Now that we have an idea of the issue, let's move to solution. 35Now that we have an idea of the issue, let's move to solution.
36 36
37* The Solution 37* The Solution
38 38
39In order to fix the issue, I waited until I was allowed to type the root 39In order to fix the issue, I waited until I was allowed to type the root user's
40user's password, and then I executed the following command: 40password, and then I executed the following command:
41 41
42#+begin_src sh 42#+begin_src sh
43nano /etc/fstab 43nano /etc/fstab
44#+end_src 44#+end_src
45 45
46Within the =fstab= file, I needed to comment/remove the following lines 46Within the =fstab= file, I needed to comment/remove the following lines at the
47at the bottom of the file. You can comment-out a line by prepending a 47bottom of the file. You can comment-out a line by prepending a =#= symbol at the
48=#= symbol at the beginning of the line. You can also delete the line 48beginning of the line. You can also delete the line entirely.
49entirely.
50 49
51#+begin_src conf 50#+begin_src conf
52# What it looked like when running into the issue: 51# What it looked like when running into the issue:
@@ -58,8 +57,8 @@ UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
58# UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0 57# UUID=E69867E59867B32B /mnt/white-02 ntfs-3g uid=1000,gid=1000 0 0
59#+end_src 58#+end_src
60 59
61Once removing the lines above from the =/etc/fstab= file, save and exit 60Once removing the lines above from the =/etc/fstab= file, save and exit the file
62the file by hitting the =Ctrl= + =x= key combo. 61by hitting the =Ctrl= + =x= key combo.
63 62
64You can now hit =Ctrl= + =D= to continue, or reboot: 63You can now hit =Ctrl= + =D= to continue, or reboot:
65 64
@@ -67,5 +66,5 @@ You can now hit =Ctrl= + =D= to continue, or reboot:
67systemctl reboot 66systemctl reboot
68#+end_src 67#+end_src
69 68
70Once rebooted, I was able to watch the machine boot properly and launch 69Once rebooted, I was able to watch the machine boot properly and launch to the
71to the TTY login screen without errors! 70TTY login screen without errors!
content/blog/2024-02-21-self-hosting-otter-wiki.org +17 −20
@@ -5,14 +5,14 @@
5 5
6* An Otter Wiki 6* An Otter Wiki
7 7
8[[https://otterwiki.com/][An Otter Wiki]] is an easy to use wiki 8[[https://otterwiki.com/][An Otter Wiki]] is an easy to use wiki software that takes almost no effort to set
9software that takes almost no effort to set up and maintain. 9up and maintain.
10 10
11* Installation 11* Installation
12 12
13To install An Otter Wiki, I'm going to use Docker Compose to create the 13To install An Otter Wiki, I'm going to use Docker Compose to create the
14container and then use Nginx as a reverse proxy to allow external access 14container and then use Nginx as a reverse proxy to allow external access via a
15via a subdomain I own. 15subdomain I own.
16 16
17** Docker Compose 17** Docker Compose
18 18
@@ -22,16 +22,14 @@ Start by creating a directory for the container's files.
22mkdir ~/otterwiki 22mkdir ~/otterwiki
23#+end_src 23#+end_src
24 24
25Next, create the =docker-compose.yml= file to define the container's 25Next, create the =docker-compose.yml= file to define the container's parameters.
26parameters.
27 26
28#+begin_src sh 27#+begin_src sh
29nano ~/otterwiki/docker-compose.yml 28nano ~/otterwiki/docker-compose.yml
30#+end_src 29#+end_src
31 30
32Within the file, paste the following content. You can read the project's 31Within the file, paste the following content. You can read the project's
33documentation if you want to further override or customize the 32documentation if you want to further override or customize the container.
34container.
35 33
36#+begin_src conf 34#+begin_src conf
37version: '3' 35version: '3'
@@ -54,22 +52,22 @@ cd ~/otterwiki
54sudo docker-compose up -d 52sudo docker-compose up -d
55#+end_src 53#+end_src
56 54
57The container is now available at =localhost:8337=. Next, we will use 55The container is now available at =localhost:8337=. Next, we will use Nginx to
58Nginx to serve this app externally. 56serve this app externally.
59 57
60** Nginx 58** Nginx
61 59
62To access the app externally, let's set up a reverse proxy. I'll start 60To access the app externally, let's set up a reverse proxy. I'll start by
63by creating the Nginx configuration file for my wiki. 61creating the Nginx configuration file for my wiki.
64 62
65#+begin_src sh 63#+begin_src sh
66sudo nano /etc/nginx/sites-available/wiki 64sudo nano /etc/nginx/sites-available/wiki
67#+end_src 65#+end_src
68 66
69Within the file, I have saved the following content. This assumes you 67Within the file, I have saved the following content. This assumes you already
70already have a TLS/SSL cert to use with this subdomain. If not, simply 68have a TLS/SSL cert to use with this subdomain. If not, simply remove the
71remove the =ssl_*= variables, remove the =80= server block, and change 69=ssl_*= variables, remove the =80= server block, and change the =443= server
72the =443= server block to =80= to serve the app without SSL. 70block to =80= to serve the app without SSL.
73 71
74#+begin_src conf 72#+begin_src conf
75server { 73server {
@@ -107,15 +105,14 @@ server {
107} 105}
108#+end_src 106#+end_src
109 107
110Save and close the configuration file. On Nginx, we need to symlink the 108Save and close the configuration file. On Nginx, we need to symlink the file to
111file to enable it. 109enable it.
112 110
113#+begin_src sh 111#+begin_src sh
114sudo ln -s /etc/nginx/sites-available/wiki /etc/nginx/sites-enabled/wiki 112sudo ln -s /etc/nginx/sites-available/wiki /etc/nginx/sites-enabled/wiki
115#+end_src 113#+end_src
116 114
117Once enabled, restart the Nginx server to start serving the app 115Once enabled, restart the Nginx server to start serving the app externally.
118externally.
119 116
120#+begin_src sh 117#+begin_src sh
121sudo systemctl restart nginx.service 118sudo systemctl restart nginx.service
content/blog/2024-03-13-doom-emacs.org +92 −113
@@ -5,9 +5,9 @@
5 5
6** Screenshots 6** Screenshots
7 7
8These screenshots are showing a project opened with projectile, a 8These screenshots are showing a project opened with projectile, a treemacs side
9treemacs side pane open with the project contents, multiple buffers 9pane open with the project contents, multiple buffers tiled next to each other,
10tiled next to each other, and the help pane open at the bottomm. 10and the help pane open at the bottomm.
11 11
12The themes are =doom-homage-white= and =doom-homage-black=. 12The themes are =doom-homage-white= and =doom-homage-black=.
13 13
@@ -19,44 +19,35 @@ The themes are =doom-homage-white= and =doom-homage-black=.
19 19
20** Getting Started 20** Getting Started
21 21
22I have been switching back and forth between 22I have been switching back and forth between [[https://en.wikipedia.org/wiki/Markdown][markdown]] and [[https://en.wikipedia.org/wiki/Org-mode][org-mode]] recently for
23[[https://en.wikipedia.org/wiki/Markdown][markdown]] and 23my personal note taking, wiki, and even this blog. As a result, I have been
24[[https://en.wikipedia.org/wiki/Org-mode][org-mode]] recently for my 24stumbling further into the world of Emacs and found myself at a point where I
25personal note taking, wiki, and even this blog. As a result, I have been 25now prefer to do most of my basic editing within Emacs.
26stumbling further into the world of Emacs and found myself at a point
27where I now prefer to do most of my basic editing within Emacs.
28 26
29I'll leave the markdown vs. org-mode debate for another post, but I love 27I'll leave the markdown vs. org-mode debate for another post, but I love
30org-mode's extensibility and interactive nature within Emacs, but it 28org-mode's extensibility and interactive nature within Emacs, but it becomes
31becomes very unwieldy in any other client implementation of org-mode - 29very unwieldy in any other client implementation of org-mode - especially on
32especially on iOS. On the flip side, markdown is limited in 30iOS. On the flip side, markdown is limited in functionality and fractured into
33functionality and fractured into different standards, but it's simple 31different standards, but it's simple and popular enough that there are a
34and popular enough that there are a plethora of great clients to choose 32plethora of great clients to choose from that will get the job done.
35from that will get the job done.
36 33
37For now, I want to focus on how I have been using Emacs and some of the 34For now, I want to focus on how I have been using Emacs and some of the things
38things that would have helped me learn it faster had I known where to 35that would have helped me learn it faster had I known where to start.
39start.
40 36
41*** Installation 37*** Installation
42 38
43This post focuses on [[https://github.com/doomemacs/doomemacs][Doom 39This post focuses on [[https://github.com/doomemacs/doomemacs][Doom Emacs]], which is an Emacs framework that provides an
44Emacs]], which is an Emacs framework that provides an alternative 40alternative experience to the vanilla [[https://www.gnu.org/software/emacs/][GNU Emacs]].
45experience to the vanilla [[https://www.gnu.org/software/emacs/][GNU
46Emacs]].
47 41
48The 42The [[https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org][Getting Start Guide]] has an extremely detailed walkthrough of installation
49[[https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org][Getting 43for all systems, so please refer to that guide for up-to-date instructions.
50Start Guide]] has an extremely detailed walkthrough of installation for
51all systems, so please refer to that guide for up-to-date instructions.
52 44
53I chose to install on macOS, using the Homebrew option with the 45I chose to install on macOS, using the Homebrew option with the
54=railwaycat/emacsmacport= version of Emacs. 46=railwaycat/emacsmacport= version of Emacs.
55 47
56Once the program is installed, you can run the program by typing =emacs= 48Once the program is installed, you can run the program by typing =emacs= in a
57in a terminal. If you installed a version of Emacs that supports both a 49terminal. If you installed a version of Emacs that supports both a GUI and TUI,
58GUI and TUI, you will have to run =emacs -nw= to get the TUI instead of 50you will have to run =emacs -nw= to get the TUI instead of the default GUI.
59the default GUI.
60 51
61*** Configuration 52*** Configuration
62 53
@@ -65,13 +56,12 @@ Once installed, you can configure Doom by editing the files within the
65 56
661. =config.el= - Personal configuration file 571. =config.el= - Personal configuration file
672. =custom.el= - Custom set variables 582. =custom.el= - Custom set variables
683. =init.el= - Doom modules and load order, must run =doom sync= after 593. =init.el= - Doom modules and load order, must run =doom sync= after modifying
69 modifying 604. =packages.el= - Declare packages to install in this file, then run =doom
704. =packages.el= - Declare packages to install in this file, then run 61 sync= to install
71 =doom sync= to install
72 62
73I only needed a few customizations for my configuration, so I'll list 63I only needed a few customizations for my configuration, so I'll list them
74them below. 64below.
75 65
76#+begin_src lisp 66#+begin_src lisp
77;; ~/.doom.d/config.el 67;; ~/.doom.d/config.el
@@ -145,30 +135,28 @@ them below.
145 (default +bindings +smartparens)) 135 (default +bindings +smartparens))
146#+end_src 136#+end_src
147 137
148If you're editing these files within Doom directly, remember to run 138If you're editing these files within Doom directly, remember to run =SPC h r r=
149=SPC h r r= to reload the configuration. Also remember to run 139to reload the configuration. Also remember to run =doom sync= for any changes to
150=doom sync= for any changes to the =init.el= or =packages.el= files. 140the =init.el= or =packages.el= files.
151 141
152** Basic Functionality 142** Basic Functionality
153 143
154I kept a cheat sheet note open at first with all of the basic functions 144I kept a cheat sheet note open at first with all of the basic functions typed
155typed out, copied as I went through the tutorial. After a little while, 145out, copied as I went through the tutorial. After a little while, I no longer
156I no longer needed it. I highly recommend writing down the most 146needed it. I highly recommend writing down the most applicable shortcuts for
157applicable shortcuts for your preferred functionality and refer back to 147your preferred functionality and refer back to it until you've memorized it.
158it until you've memorized it. 148
159 149Memorizing the shortcuts will differ based on the type of Emacs framework being
160Memorizing the shortcuts will differ based on the type of Emacs 150used. Personally, migrating from vanilla Emacs to Doom Emacs simplified
161framework being used. Personally, migrating from vanilla Emacs to Doom 151everything by a large factor and instantly enabled me to start working on my
162Emacs simplified everything by a large factor and instantly enabled me 152projects, eliminating most of the hurdles I was running into. The vanilla emacs
163to start working on my projects, eliminating most of the hurdles I was 153hotkeys became obnoxious and I actually stopped using Emacs entirely for about a
164running into. The vanilla emacs hotkeys became obnoxious and I actually 154month before trying Doom.
165stopped using Emacs entirely for about a month before trying Doom. 155
166 156For me, the first logical step is to interact with the local filesystem. To do
167For me, the first logical step is to interact with the local filesystem. 157this, I needed to know how to open directories, open files, save files, discard
168To do this, I needed to know how to open directories, open files, save 158changes, close files, and switch between open files. Here are some example
169files, discard changes, close files, and switch between open files. Here 159shortcuts I've written down in order to accomplish file-based actions.
170are some example shortcuts I've written down in order to accomplish
171file-based actions.
172 160
173| Doom Hotkey | Emacs Hotkey | Description | 161| Doom Hotkey | Emacs Hotkey | Description |
174|-----------------+---------------+----------------------------------------| 162|-----------------+---------------+----------------------------------------|
@@ -181,30 +169,28 @@ file-based actions.
181| =SPC b k= | =C-x k= | Kill current buffer | 169| =SPC b k= | =C-x k= | Kill current buffer |
182| =SPC w h/j/k/l= | =C-x o=[fn:2] | Move left/down/up/right to next buffer | 170| =SPC w h/j/k/l= | =C-x o=[fn:2] | Move left/down/up/right to next buffer |
183 171
184In general, when in Doom, you can press =SPC= and wait a second for the 172In general, when in Doom, you can press =SPC= and wait a second for the help
185help pane to appear with all available hotkey options. For example, you 173pane to appear with all available hotkey options. For example, you can press
186can press =SPC=, wait for the help pane, and then select a key such as 174=SPC=, wait for the help pane, and then select a key such as =g= to enter the
187=g= to enter the git help pane and explore further command options. 175git help pane and explore further command options.
188 176
189** Editing 177** Editing
190 178
191Next in my process is to dive into editing for any languages I'm 179Next in my process is to dive into editing for any languages I'm currently
192currently using. In this post, I will just cover Markdown and Org-Mode 180using. In this post, I will just cover Markdown and Org-Mode but I have also
193but I have also been slowly adoping some Python and general web dev 181been slowly adoping some Python and general web dev tools as well.
194tools as well.
195 182
196*** Markdown 183*** Markdown
197 184
198#+caption: Markdown Preview 185#+caption: Markdown Preview
199[[https://img.cleberg.net/blog/20240314-doom-emacs/markdown.png]] 186[[https://img.cleberg.net/blog/20240314-doom-emacs/markdown.png]]
200 187
201Markdown is fairly simple as the syntax is limited, so just make sure 188Markdown is fairly simple as the syntax is limited, so just make sure the
202the =~/.doom.d/init.el= includes the =markdown= declaration in the 189=~/.doom.d/init.el= includes the =markdown= declaration in the =:lang= section.
203=:lang= section.
204 190
205This package includes the following hotkey menus. The insert and toggle 191This package includes the following hotkey menus. The insert and toggle menu
206menu expands further, allowing you to insert various markdown elements 192expands further, allowing you to insert various markdown elements and toggle
207and toggle things like link hiding. 193things like link hiding.
208 194
209| Doom Hotkey | Function | 195| Doom Hotkey | Function |
210|------------------------------+--------------------------| 196|------------------------------+--------------------------|
@@ -221,14 +207,12 @@ and toggle things like link hiding.
221#+caption: Org-Mode Preview 207#+caption: Org-Mode Preview
222[[https://img.cleberg.net/blog/20240314-doom-emacs/org.png]] 208[[https://img.cleberg.net/blog/20240314-doom-emacs/org.png]]
223 209
224Similar to the markdown section above, ensure that the 210Similar to the markdown section above, ensure that the =~/.doom.d/init.el=
225=~/.doom.d/init.el= includes the =org= declaration in the =:lang= 211includes the =org= declaration in the =:lang= section.
226section.
227 212
228There are a few hot keys, but a quick search with =SPC : org= shows that 213There are a few hot keys, but a quick search with =SPC : org= shows that there
229there are 865 possible org-related functions you can run. I won't 214are 865 possible org-related functions you can run. I won't possibly be able to
230possibly be able to list them all, so I will simply cover a few of the 215list them all, so I will simply cover a few of the basic commands I use myself.
231basic commands I use myself.
232 216
233| Doom Hotkey | Function | 217| Doom Hotkey | Function |
234|----------------+---------------------------------------| 218|----------------+---------------------------------------|
@@ -246,32 +230,28 @@ basic commands I use myself.
246 230
2471. Org-Publish 2311. Org-Publish
248 232
249 Org includes a 233 Org includes a [[https://orgmode.org/manual/Publishing.html][publishing management system]] by default that allows you to
250 [[https://orgmode.org/manual/Publishing.html][publishing management 234 export org files to Org, iCalendar, HTML, LaTex, Markdown, ODT, and Plain
251 system]] by default that allows you to export org files to Org, 235 Text. Most of these can be exported into another buffer and opened, or simply
252 iCalendar, HTML, LaTex, Markdown, ODT, and Plain Text. Most of these 236 to an external file.
253 can be exported into another buffer and opened, or simply to an
254 external file.
255 237
256 While inside an org file, simply run =SPC m e= or 238 While inside an org file, simply run =SPC m e= or =M-x org-export-dispatch=
257 =M-x org-export-dispatch= to open the export menu. This menu will 239 to open the export menu. This menu will show all options and ask you to
258 show all options and ask you to select an option. If you want to 240 select an option. If you want to export to HTML, simply press =h= and then
259 export to HTML, simply press =h= and then =H= (As HTML buffer), =h= 241 =H= (As HTML buffer), =h= (As HTML file), or =o= (As HTML file and open).
260 (As HTML file), or =o= (As HTML file and open).
261 242
2622. Projects 2432. Projects
263 244
264 Some publishing options are easier with a defined project in Emacs. 245 Some publishing options are easier with a defined project in Emacs. To create
265 To create a project within Emacs, I use two methods: 246 a project within Emacs, I use two methods:
266 247
267 1. Add the project via the projectile command =SPC p a=. Does not 248 1. Add the project via the projectile command =SPC p a=. Does not always work
268 always work for me. 249 for me.
269 2. Add an empty =.projectile= file in the project root. 250 2. Add an empty =.projectile= file in the project root.
270 251
271 Once a project has been created, you can create custom publishing 252 Once a project has been created, you can create custom publishing actions
272 actions within your =~/.doom.d/config.el= file. For example, here's a 253 within your =~/.doom.d/config.el= file. For example, here's a test project I
273 test project I created to try and convert this blog to org-mode 254 created to try and convert this blog to org-mode recently.
274 recently.
275 255
276 #+begin_src lisp 256 #+begin_src lisp
277 ;; org-publish 257 ;; org-publish
@@ -342,19 +322,18 @@ basic commands I use myself.
342 322
343** General Thoughts 323** General Thoughts
344 324
345I have enjoyed Doom Emacs (far more than GNU Emacs) and will likely 325I have enjoyed Doom Emacs (far more than GNU Emacs) and will likely continue to
346continue to use it as my main editor for the time being. Org-Mode is 326use it as my main editor for the time being. Org-Mode is certainly the largest
347certainly the largest factor here, as I far prefer it over Markdown due 327factor here, as I far prefer it over Markdown due to its inherent features and
348to its inherent features and detailed markup options. However, working 328detailed markup options. However, working with org-mode on iOS has been a pain
349with org-mode on iOS has been a pain and I will have to see if there's 329and I will have to see if there's an easier way to resolve those issues or if
350an easier way to resolve those issues or if going back to separate 330going back to separate Markdown, Reminders, and Calendar apps is easier to work
351Markdown, Reminders, and Calendar apps is easier to work with than an 331with than an all-in-one org solution.
352all-in-one org solution. 332
353 333[fn:1] Doom's evil-window functionality is a bit different from GNU Emacs, but
354[fn:1] Doom's evil-window functionality is a bit different from GNU 334 you can always switch to the "other" buffer with =C-x o= or =C-x b= to
355 Emacs, but you can always switch to the "other" buffer with 335 get a list of buffers to select.
356 =C-x o= or =C-x b= to get a list of buffers to select. 336
357 337[fn:2] Doom's evil-window functionality is a bit different from GNU Emacs, but
358[fn:2] Doom's evil-window functionality is a bit different from GNU 338 you can always switch to the "other" buffer with =C-x o= or =C-x b= to
359 Emacs, but you can always switch to the "other" buffer with 339 get a list of buffers to select.
360 =C-x o= or =C-x b= to get a list of buffers to select.
content/blog/2024-03-15-self-hosting-ddns-updater.org +41 −52
@@ -6,25 +6,21 @@
6#+caption: DDNS Updater Web View 6#+caption: DDNS Updater Web View
7[[https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png]] 7[[https://img.cleberg.net/blog/20240315-ddns-updater/ddns.png]]
8 8
9[[https://github.com/qdm12/ddns-updater][DDNS Updater]] is a program to 9[[https://github.com/qdm12/ddns-updater][DDNS Updater]] is a program to keep DNS A and/or AAAA records updated for multiple
10keep DNS A and/or AAAA records updated for multiple DNS providers. 10DNS providers.
11 11
12If you've read any of my other posts, you'll notice that I have been 12If you've read any of my other posts, you'll notice that I have been searching
13searching for and using a few different DDNS updating solutions for 13for and using a few different DDNS updating solutions for years. You'll also
14years. You'll also notice that I love any projects that offer a Docker 14notice that I love any projects that offer a Docker Compose solution.
15Compose solution.
16 15
17Luckily, DDNS Upater fits both of these preferences. 16Luckily, DDNS Upater fits both of these preferences.
18 17
19** Installation 18** Installation
20 19
21To get started, always make sure to review the project's 20To get started, always make sure to review the project's [[https://github.com/qdm12/ddns-updater/blob/master/README.md][README]]. I'll be
22[[https://github.com/qdm12/ddns-updater/blob/master/README.md][README]]. 21documenting my steps below, but they may have changed by the time you read this.
23I'll be documenting my steps below, but they may have changed by the
24time you read this.
25 22
26The first step is to set up the directories and files required for the 23The first step is to set up the directories and files required for the project.
27project.
28 24
29#+begin_src sh 25#+begin_src sh
30mkdir ~/ddns-updater 26mkdir ~/ddns-updater
@@ -34,43 +30,36 @@ touch ~/ddns-updater/data/config.json
34 30
35*** Configuration 31*** Configuration
36 32
37The main configuration you need to update is the =data/config.json= 33The main configuration you need to update is the =data/config.json= file. There
38file. There is a large list of supported providers in the README, but 34is a large list of supported providers in the README, but I'm going to use
39I'm going to use Cloudflare in this example. 35Cloudflare in this example.
40 36
41#+begin_src sh 37#+begin_src sh
42nano ~/ddns-updater/data/config.json 38nano ~/ddns-updater/data/config.json
43#+end_src 39#+end_src
44 40
45When setting up the configuration for Cloudflare, you'll need the 41When setting up the configuration for Cloudflare, you'll need the following:
46following:
47 42
48- Required Parameters 43- Required Parameters
49 - ="zone_identifier"= is the Zone ID of your site from the domain 44 - ="zone_identifier"= is the Zone ID of your site from the domain overview
50 overview page 45 page
51 - ="host"= is your host and can be ="@"=, a subdomain or the wildcard 46 - ="host"= is your host and can be ="@"=, a subdomain or the wildcard ="*"=.
52 ="*"=. See 47 See [[https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949][this issue comment for context]].
53 [[https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949][this 48 - ="ttl"= integer value for record TTL in seconds (specify 1 for automatic)
54 issue comment for context]]. 49 - One of the following ([[https://developers.cloudflare.com/fundamentals/api/get-started/][how to find API keys]]):
55 - ="ttl"= integer value for record TTL in seconds (specify 1 for
56 automatic)
57 - One of the following
58 ([[https://developers.cloudflare.com/fundamentals/api/get-started/][how
59 to find API keys]]):
60 - Email ="email"= and Global API Key ="key"= 50 - Email ="email"= and Global API Key ="key"=
61 - User service key ="user_service_key"= 51 - User service key ="user_service_key"=
62 - API Token ="token"=, configured with DNS edit permissions for your 52 - API Token ="token"=, configured with DNS edit permissions for your DNS
63 DNS name's zone 53 name's zone
64- Optional Parameters 54- Optional Parameters
65 - ="proxied"= can be set to =true= to use the proxy services of 55 - ="proxied"= can be set to =true= to use the proxy services of Cloudflare
66 Cloudflare 56 - ="ip_version"= can be =ipv4= (A records), or =ipv6= (AAAA records) or =ipv4
67 - ="ip_version"= can be =ipv4= (A records), or =ipv6= (AAAA records) 57 or ipv6= (update one of the two, depending on the public ip found). It
68 or =ipv4 or ipv6= (update one of the two, depending on the public 58 defaults to =ipv4 or ipv6=.
69 ip found). It defaults to =ipv4 or ipv6=. 59 - ="ipv6_suffix"= is the IPv6 interface identifier suffix to use. It can be
70 - ="ipv6_suffix"= is the IPv6 interface identifier suffix to use. It 60 for example =0:0:0:0:72ad:8fbb:a54e:bedd/64=. If left empty, it defaults to
71 can be for example =0:0:0:0:72ad:8fbb:a54e:bedd/64=. If left empty, 61 no suffix and the raw public IPv6 address obtained is used in the record
72 it defaults to no suffix and the raw public IPv6 address obtained is 62 updating.
73 used in the record updating.
74 63
75#+begin_src conf 64#+begin_src conf
76{ 65{
@@ -90,8 +79,8 @@ following:
90} 79}
91#+end_src 80#+end_src
92 81
93Once you have configured the provider of your choice, correct the file 82Once you have configured the provider of your choice, correct the file and
94and directory permissions and ownership. 83directory permissions and ownership.
95 84
96#+begin_src sh 85#+begin_src sh
97cd ~/ddns_updater 86cd ~/ddns_updater
@@ -105,8 +94,8 @@ chmod 400 data/config.json
105 94
106*** Docker Compose 95*** Docker Compose
107 96
108After creating the project structure, let's create the 97After creating the project structure, let's create the =docker-compose.yml=
109=docker-compose.yml= file. 98file.
110 99
111#+begin_src sh 100#+begin_src sh
112nano ~/ddns_-pdater/docker-compose.yml 101nano ~/ddns_-pdater/docker-compose.yml
@@ -150,22 +139,22 @@ services:
150 restart: always 139 restart: always
151#+end_src 140#+end_src
152 141
153After configuring your preferences in the =docker-compose.yml=, launch 142After configuring your preferences in the =docker-compose.yml=, launch the
154the container. 143container.
155 144
156#+begin_src sh 145#+begin_src sh
157cd ~/ddns-updater 146cd ~/ddns-updater
158sudo docker-compose up -d 147sudo docker-compose up -d
159#+end_src 148#+end_src
160 149
161If you've launched this on your local machine, you can launch 150If you've launched this on your local machine, you can launch =localhost:8097=
162=localhost:8097= in your browser to see the results. 151in your browser to see the results.
163 152
164*** Nginx Reverse Proxy 153*** Nginx Reverse Proxy
165 154
166If you launched this service on a server, other machine, or just want to 155If you launched this service on a server, other machine, or just want to access
167access it remotely via a domain name, you can use Nginx as a reverse 156it remotely via a domain name, you can use Nginx as a reverse proxy to expose
168proxy to expose the service publicly. 157the service publicly.
169 158
170Start by creating the Nginx configuration file. 159Start by creating the Nginx configuration file.
171 160
@@ -193,8 +182,8 @@ server {
193} 182}
194#+end_src 183#+end_src
195 184
196Here's a full example that uses my Authelia authentication service to 185Here's a full example that uses my Authelia authentication service to require
197require authentication before someone can access the web page. 186authentication before someone can access the web page.
198 187
199#+begin_src conf 188#+begin_src conf
200server { 189server {
content/blog/2024-03-29-org-blog.org +56 −67
@@ -3,48 +3,41 @@
3#+description: 3#+description:
4#+slug: org-blog 4#+slug: org-blog
5 5
6First and foremost, apologies to those who subscribe via RSS as I know 6First and foremost, apologies to those who subscribe via RSS as I know that my
7that my feed duplicated itself when I moved this blog over to org-mode 7feed duplicated itself when I moved this blog over to org-mode last night.
8last night.
9 8
10This post focuses specifically on the configuration and tools I use to 9This post focuses specifically on the configuration and tools I use to blog from
11blog from Emacs with Org-Mode and does not focus on Emacs or Org-Mode 10Emacs with Org-Mode and does not focus on Emacs or Org-Mode themselves. Refer to
12themselves. Refer to the post I wrote about 11the post I wrote about [[https://cleberg.net/blog/doom-emacs-org-mode.html][Doom Emacs & Org-Mode]] for more information about my base
13[[https://cleberg.net/blog/doom-emacs-org-mode.html][Doom Emacs & Org-Mode]] for 12Emacs configuration.
14more information about my base Emacs configuration.
15 13
16* Weblorg 14* Weblorg
17 15
18The first step in blogging with Org-Mode is to choose a method to 16The first step in blogging with Org-Mode is to choose a method to convert the
19convert the source files to HTML and publish them. The Worg site 17source files to HTML and publish them. The Worg site maintains a nice list of
20maintains a nice list of 18[[https://orgmode.org/worg/org-blog-wiki.html][Blogs and Wikis with Org]], but the tools are inevitably different and
21[[https://orgmode.org/worg/org-blog-wiki.html][Blogs and Wikis with 19opinionated, so you'll need to find what works for you.
22Org]], but the tools are inevitably different and opinionated, so you'll 20
23need to find what works for you. 21I tried using Jekyll, Hugo, ox-hugo, Nikola, Blorg, org-static-blog, and the
24 22native org-publish functions before finally settling on Weblorg. For one reason
25I tried using Jekyll, Hugo, ox-hugo, Nikola, Blorg, org-static-blog, and 23or another, the other solutions were a drastic step down from my previous
26the native org-publish functions before finally settling on Weblorg. For 24workflow that used [[https://www.getzola.org/][Zola]] with Markdown content.
27one reason or another, the other solutions were a drastic step down from 25
28my previous workflow that used [[https://www.getzola.org/][Zola]] with 26[[https://github.com/emacs-love/weblorg][Weblorg]] is a static site generator for [[https://orgmode.org/][org-mode]], built for use within [[https://www.gnu.org/software/emacs/][Emacs]].
29Markdown content. 27Since it's written in Emacs Lisp, there's no need to install other languages or
30 28frameworks to get started. More than that, you can write in any editor you
31[[https://github.com/emacs-love/weblorg][Weblorg]] is a static site 29please and simply invoke the Emacs build process with the =--script= parameter
32generator for [[https://orgmode.org/][org-mode]], built for use within
33[[https://www.gnu.org/software/emacs/][Emacs]]. Since it's written in
34Emacs Lisp, there's no need to install other languages or frameworks to
35get started. More than that, you can write in any editor you please and
36simply invoke the Emacs build process with the =--script= parameter
37instead of requiring you to blog inside Emacs. 30instead of requiring you to blog inside Emacs.
38 31
39** Installation 32** Installation
40 33
41The [[https://emacs.love/weblorg/doc/index.html][Getting Started]] page 34The [[https://emacs.love/weblorg/doc/index.html][Getting Started]] page details broad installation requirements. I am using
42details broad installation requirements. I am using Doom Emacs on macOS, 35Doom Emacs on macOS, which requires you to add the package to the
43which requires you to add the package to the =~/.doom.d/packages.el= 36=~/.doom.d/packages.el= file and configure the =publish.el= file slightly
44file and configure the =publish.el= file slightly differently. 37differently.
45 38
46To start, add the =htmlize= and =weblorg= packages to Doom, sync the 39To start, add the =htmlize= and =weblorg= packages to Doom, sync the changes,
47changes, and reload. 40and reload.
48 41
49#+begin_src sh 42#+begin_src sh
50nano ~/.doom.d/packages.el 43nano ~/.doom.d/packages.el
@@ -63,18 +56,17 @@ Either re-open Emacs or hit =SPC h r r= to reload the changes.
63 56
64** Configuration 57** Configuration
65 58
66Now that I've installed weblorg, I need to configure the project. I'll 59Now that I've installed weblorg, I need to configure the project. I'll start by
67start by navigating to my site's source code and creating a =publish.el= 60navigating to my site's source code and creating a =publish.el= file.
68file.
69 61
70#+begin_src sh 62#+begin_src sh
71cd ~/Source/cleberg.net && nano publish.el 63cd ~/Source/cleberg.net && nano publish.el
72#+end_src 64#+end_src
73 65
74Since I'm using Doom, Emacs will not automatically load the packages I 66Since I'm using Doom, Emacs will not automatically load the packages I need
75need later in the build process. To compensate, my =publish.el= file 67later in the build process. To compensate, my =publish.el= file needs to
76needs to explicitly tell Emacs where Doom stores the =htmlize=, 68explicitly tell Emacs where Doom stores the =htmlize=, =weblorg=, and
77=weblorg=, and =templatel= packages. 69=templatel= packages.
78 70
79#+begin_src lisp 71#+begin_src lisp
80;; explicity load packages since I'm using Doom Emacs 72;; explicity load packages since I'm using Doom Emacs
@@ -184,12 +176,10 @@ needs to explicitly tell Emacs where Doom stores the =htmlize=,
184** Structure 176** Structure
185 177
186The project structure for weblorg is highly customizable and the main 178The project structure for weblorg is highly customizable and the main
187restriction is that the =publish.el= file must point to the correct 179restriction is that the =publish.el= file must point to the correct paths.
188paths.
189 180
190For my blog, I prefer to keep the blog content out of the top-level 181For my blog, I prefer to keep the blog content out of the top-level directory.
191directory. This results in the following structure (shortened for 182This results in the following structure (shortened for brevity):
192brevity):
193 183
194#+begin_src txt 184#+begin_src txt
195.build/ 185.build/
@@ -217,17 +207,17 @@ build.sh
217publish.el 207publish.el
218#+end_src 208#+end_src
219 209
220This is simply my preferred structure and you can alter it to fit your 210This is simply my preferred structure and you can alter it to fit your needs.
221needs. The key here really is that you can customize at will, as long as 211The key here really is that you can customize at will, as long as the
222the =publish.el= file matches. 212=publish.el= file matches.
223 213
224** Build & Deploy 214** Build & Deploy
225 215
226Once you're content with the status of the project, you're ready to 216Once you're content with the status of the project, you're ready to build and
227build and deploy the blog. 217deploy the blog.
228 218
229My process utilizes a =build.sh= script that combines the steps I take 219My process utilizes a =build.sh= script that combines the steps I take every
230every time. 220time.
231 221
232#+begin_src sh 222#+begin_src sh
233touch build.sh && chmod +x build.sh && nano build.sh 223touch build.sh && chmod +x build.sh && nano build.sh
@@ -235,16 +225,16 @@ touch build.sh && chmod +x build.sh && nano build.sh
235 225
236Within this script, I do the following: 226Within this script, I do the following:
237 227
2381. Remove any files within the =.build= directory that I use to store 2281. Remove any files within the =.build= directory that I use to store published
239 published files. 229 files.
2402. Set the environment variable to =prod= to ensure the =base_url= 2302. Set the environment variable to =prod= to ensure the =base_url= matches my
241 matches my configuration in =publish.el=. 231 configuration in =publish.el=.
2423. Build the site with Emacs & =publish.el=. 2323. Build the site with Emacs & =publish.el=.
2434. Use =scp= to copy files to my site's public directory on my server. 2334. Use =scp= to copy files to my site's public directory on my server.
244 234
245#+begin_src sh 235#+begin_src sh
246rm -rf .build/* && \ 236rm -rf .build/* && \
247ENV=prod emacs --script publish.el && \ 237ENV=prod emacs--script publish.el && \
248scp -r .build/* ubuntu:/var/www/cleberg.net/ 238scp -r .build/* ubuntu:/var/www/cleberg.net/
249#+end_src 239#+end_src
250 240
@@ -252,13 +242,12 @@ scp -r .build/* ubuntu:/var/www/cleberg.net/
252 242
253My only current complaints are: 243My only current complaints are:
254 244
2551. Errors messages are not helpful. It takes work to determine what the 2451. Errors messages are not helpful. It takes work to determine what the error is
256 error is and where it's coming from. I generally have to sit and 246 and where it's coming from. I generally have to sit and watch the build
257 watch the build process to see the file that weblorg pubslishes right 247 process to see the file that weblorg pubslishes right before the error
258 before the error occurred. 248 occurred.
2592. The build process re-builds every single file on each run, which 2492. The build process re-builds every single file on each run, which takes a long
260 takes a long time for a blog of my size. See below for the last time 250 time for a blog of my size. See below for the last time I measured.
261 I measured.
262 251
263#+begin_src sh 252#+begin_src sh
264> time ./build.sh 253> time ./build.sh
@@ -266,5 +255,5 @@ My only current complaints are:
266./build.sh 35.46s user 0.59s system 85% cpu 41.965 total 255./build.sh 35.46s user 0.59s system 85% cpu 41.965 total
267#+end_src 256#+end_src
268 257
269Overall, I have thoroughly enjoyed using weblog and will continue to use 258Overall, I have thoroughly enjoyed using weblog and will continue to use it
270it going forward until I find something better. 259going forward until I find something better.
content/blog/2024-04-06-convert-onenote-to-markdown.org +41 −47
@@ -3,54 +3,49 @@
3#+description: 3#+description:
4#+slug: convert-onenote-to-markdown 4#+slug: convert-onenote-to-markdown
5 5
6If you're looking to convert your OneNote content to another format, 6If you're looking to convert your OneNote content to another format, such as
7such as Markdown or Org-Mode, you're in luck. I use a solution that 7Markdown or Org-Mode, you're in luck. I use a solution that doesn't require
8doesn't require other programs, such as Evernote or Notion. Personally, 8other programs, such as Evernote or Notion. Personally, I used this solution on
9I used this solution on a managed corporate laptop that doesn't allow 9a managed corporate laptop that doesn't allow installation of other programs
10installation of other programs like these. 10like these.
11 11
12This solution uses OneNote and Pandoc on Windows 10. 12This solution uses OneNote and Pandoc on Windows 10.
13 13
14* Export OneNote Content to Word 14* Export OneNote Content to Word
15 15
16To start, export any pages or tabs from OneNote to the Word format 16To start, export any pages or tabs from OneNote to the Word format (=.docx=):
17(=.docx=):
18 17
191. Open OneNote desktop. 181. Open OneNote desktop.
202. Select =File= and then =Export=. 192. Select =File= and then =Export=.
213. Select the scope of content to export, such as =Tab= or =Page=. 203. Select the scope of content to export, such as =Tab= or =Page=.
224. Name and save the file in an easy to remember location. I recommend 214. Name and save the file in an easy to remember location. I recommend your
23 your Downloads or Desktop folder. 22 Downloads or Desktop folder.
24 23
25See below for a visual walkthrough of the export process. 24See below for a visual walkthrough of the export process.
26 25
27* Download Pandoc 26* Download Pandoc
28 27
29Start by downloading Pandoc from their 28Start by downloading Pandoc from their [[https://github.com/jgm/pandoc/releases][GitHub releases]] page. I cannot install
30[[https://github.com/jgm/pandoc/releases][GitHub releases]] page. I 29=.msi= files on my corporate laptop, so I downloaded the
31cannot install =.msi= files on my corporate laptop, so I downloaded the 30=pandoc-3.1.12.3-windows-x86_64.zip= file, which contains a simple =.exe= file
32=pandoc-3.1.12.3-windows-x86_64.zip= file, which contains a simple 31that you do not need to install - you will simply run it from the command line
33=.exe= file that you do not need to install - you will simply run it 32below.
34from the command line below.
35 33
36Once downloaded, unzip the archive and move the =pandoc.exe= file to the 34Once downloaded, unzip the archive and move the =pandoc.exe= file to the same
37same folder where your Word documents were saved above. If you prefer, 35folder where your Word documents were saved above. If you prefer, you can move
38you can move this file to an easier location, such as 36this file to an easier location, such as =C:\Users\youruser\Downloads=.
39=C:\Users\youruser\Downloads=.
40 37
41* Convert Word to Markdown 38* Convert Word to Markdown
42 39
43In this example, I will be converting the Word documents to Markdown, 40In this example, I will be converting the Word documents to Markdown, but Pandoc
44but Pandoc supports 41supports [[https://github.com/jgm/pandoc?tab=readme-ov-file#the-universal-markup-converter][a ton of different formats for conversion]]. Choose the format you prefer
45[[https://github.com/jgm/pandoc?tab=readme-ov-file#the-universal-markup-converter][a
46ton of different formats for conversion]]. Choose the format you prefer
47and then modify the following commands as needed. 42and then modify the following commands as needed.
48 43
49To perform the conversion, open the Command Prompt. If you can't find 44To perform the conversion, open the Command Prompt. If you can't find it, open
50it, open the start menu and search for it. 45the start menu and search for it.
51 46
52Within the command prompt, navigate to the directory where you stored 47Within the command prompt, navigate to the directory where you stored the
53the =pandoc.exe= file and the Word documents. 48=pandoc.exe= file and the Word documents.
54 49
55#+begin_src ps1 50#+begin_src ps1
56cd "C:\Users\yourusername\Downloads" 51cd "C:\Users\yourusername\Downloads"
@@ -63,9 +58,9 @@ command.
63dir 58dir
64#+end_src 59#+end_src
65 60
66Once you have verified that you have the command prompt open in the 61Once you have verified that you have the command prompt open in the correct
67correct directory with the =pandoc.exe= and the Word documents, you can 62directory with the =pandoc.exe= and the Word documents, you can run the
68run the following loop to convert all Word documents to Markdown. 63following loop to convert all Word documents to Markdown.
69 64
70#+begin_src ps1 65#+begin_src ps1
71for %f in (*.docx) do (pandoc.exe --extract-media=. --wrap=preserve "%f" -o "%f.md") 66for %f in (*.docx) do (pandoc.exe --extract-media=. --wrap=preserve "%f" -o "%f.md")
@@ -73,25 +68,24 @@ for %f in (*.docx) do (pandoc.exe --extract-media=. --wrap=preserve "%f" -o "%f.
73 68
74This loop will perform the following actions: 69This loop will perform the following actions:
75 70
761. Find all documents matching the pattern =*.docx=, which means all 711. Find all documents matching the pattern =*.docx=, which means all Word
77 Word documents ending with that file extension. 72 documents ending with that file extension.
782. Iterate through all files found in step 1. 732. Iterate through all files found in step 1.
793. For each file, perform the pandoc command. 743. For each file, perform the pandoc command.
804. Within the pandoc command, =--extract-media= saves all media found in 754. Within the pandoc command, =--extract-media= saves all media found in the
81 the files to the current folder, with pandoc automatically creating a 76 files to the current folder, with pandoc automatically creating a =media=
82 =media= subfolder to hold all images. 77 subfolder to hold all images.
835. Within the pandoc command, =--wrap=preserve= will attempt to prseerve 785. Within the pandoc command, =--wrap=preserve= will attempt to prseerve the
84 the wrapping from the source document. 79 wrapping from the source document.
856. Within the pandoc command, the final step is to specify the output 806. Within the pandoc command, the final step is to specify the output path with
86 path with =-o=. This option adds the =.md= file extension to 81 =-o=. This option adds the =.md= file extension to recognize the output files
87 recognize the output files as Markdown files. 82 as Markdown files.
88 83
89If you want to export to another format, simply specify the 84If you want to export to another format, simply specify the =-f==/=--from== and
90=-f==/=--from== and =-t==/=--to== options. 85=-t==/=--to== options.
91 86
92For example, you can convert the Word document to org-mode. You can also 87For example, you can convert the Word document to org-mode. You can also convert
93convert to one format and subsequently convert to other formats as 88to one format and subsequently convert to other formats as needed.
94needed.
95 89
96#+begin_src ps1 90#+begin_src ps1
97pandoc.exe -f docx -t org file.docx 91pandoc.exe -f docx -t org file.docx
content/blog/2024-04-08-docker-local-web-server.org +35 −38
@@ -3,43 +3,42 @@
3#+description: 3#+description:
4#+slug: docker-local-web-server 4#+slug: docker-local-web-server
5 5
6When developing websites locally, I often use a simple Python web server 6When developing websites locally, I often use a simple Python web server to
7to observe the changes. 7observe the changes.
8 8
9#+begin_src sh 9#+begin_src sh
10python3 -m http.server 10python3 -m http.server
11#+end_src 11#+end_src
12 12
13However, this approach has its limitations. For example, this approach 13However, this approach has its limitations. For example, this approach does not
14does not enable logging or access controls. You also need to customize 14enable logging or access controls. You also need to customize =SimpleHTTPServer=
15=SimpleHTTPServer= if you have advanced needs from your web server. 15if you have advanced needs from your web server.
16 16
17So, I went to find an alternative that is almost as easy and far more 17So, I went to find an alternative that is almost as easy and far more extensible
18extensible and found Docker Desktop to be a suitable replacement. 18and found Docker Desktop to be a suitable replacement.
19 19
20* Docker Desktop 20* Docker Desktop
21 21
22** Installation 22** Installation
23 23
24[[https://www.docker.com/products/docker-desktop/][Docker Desktop]] is a 24[[https://www.docker.com/products/docker-desktop/][Docker Desktop]] is a desktop GUI for the phenomenal Docker container software.
25desktop GUI for the phenomenal Docker container software. This allows 25This allows you to manage containers, images, volumes, environments, and
26you to manage containers, images, volumes, environments, and extensions 26extensions via an easy-to-use GUI.
27via an easy-to-use GUI.
28 27
29To install, open the link above and click the =Download= button for your 28To install, open the link above and click the =Download= button for your
30platform. I'm going through this process on an M2 Macbook, so I 29platform. I'm going through this process on an M2 Macbook, so I downloaded the
31downloaded the Mac - Apple Chip version. 30Mac - Apple Chip version.
32 31
33Open the installer and follow the installation process until the 32Open the installer and follow the installation process until the application
34application finishes the installation process. 33finishes the installation process.
35 34
36#+caption: Docker Desktop on macOS 35#+caption: Docker Desktop on macOS
37[[https://img.cleberg.net/blog/20240408-docker-local-web-server/docker-desktop.png]] 36[[https://img.cleberg.net/blog/20240408-docker-local-web-server/docker-desktop.png]]
38 37
39** Creating an Nginx Container 38** Creating an Nginx Container
40 39
41I prefer to use the command line to create containers, so the following 40I prefer to use the command line to create containers, so the following commands
42commands will be input via the terminal. 41will be input via the terminal.
43 42
44The following command will create a container, using the =nginx= image: 43The following command will create a container, using the =nginx= image:
45 44
@@ -61,20 +60,20 @@ You can navigate to [[http://localhost:8000]] to see the resulting page.
61 60
62** Customizing the Nginx Container 61** Customizing the Nginx Container
63 62
64Now that I have a container running the Nginx web server, I need to link 63Now that I have a container running the Nginx web server, I need to link some
65some volumes so that I can modify the site configuration and provide the 64volumes so that I can modify the site configuration and provide the web files to
66web files to serve. 65serve.
67 66
68Let's start with the new command, which adds two volumes: 67Let's start with the new command, which adds two volumes:
69 68
701. =<your_content>:/usr/share/nginx/html=: This is the directory where 691. =<your_content>:/usr/share/nginx/html=: This is the directory where you will
71 you will provide the web pages for the server to serve. 70 provide the web pages for the server to serve.
722. =<your_config>:/etc/nginx/conf.d/default.conf=: This is the Nginx 712. =<your_config>:/etc/nginx/conf.d/default.conf=: This is the Nginx
73 configuration file for your site. 72 configuration file for your site.
74 73
75To see the updates, you can delete the previous container in the GUI or 74To see the updates, you can delete the previous container in the GUI or run
76run =docker stop web= to stop the container. Once stopped, you can run 75=docker stop web= to stop the container. Once stopped, you can run the new
77the new =docker run= command below. 76=docker run= command below.
78 77
79#+begin_src sh 78#+begin_src sh
80docker run -it -d -p 8000:80 --name web -v ~/Source/cleberg.net/.build:/usr/share/nginx/html -v ~/Source/cleberg.net/nginx-config.conf:/etc/nginx/conf.d/default.conf nginx 79docker run -it -d -p 8000:80 --name web -v ~/Source/cleberg.net/.build:/usr/share/nginx/html -v ~/Source/cleberg.net/nginx-config.conf:/etc/nginx/conf.d/default.conf nginx
@@ -107,16 +106,15 @@ server {
107 106
108* Customizing Deployment Actions 107* Customizing Deployment Actions
109 108
110I am currently blogging with [[https://emacs.love/weblorg/][weblorg]], 109I am currently blogging with [[https://emacs.love/weblorg/][weblorg]], which uses a custom =publish.el= file to
111which uses a custom =publish.el= file to build the static site. Within 110build the static site. Within this file, I have configured my deployment process
112this file, I have configured my deployment process to check for the 111to check for the =ENV= variable in thesh and if it's set to =prod=, the script
113=ENV= variable in thesh and if it's set to =prod=, the script will set 112will set the base URLs to =https://cleberg.net=. If not, it sets the base URLs
114the base URLs to =https://cleberg.net=. If not, it sets the base URLs to 113to =localhost:8000= (which matches the port used in the container above).
115=localhost:8000= (which matches the port used in the container above).
116 114
117Therefore, I have modified my =build.sh= script to build with 115Therefore, I have modified my =build.sh= script to build with =localhost= URLs
118=localhost= URLs if =ENV= is not set to =prod=. It also prevents the 116if =ENV= is not set to =prod=. It also prevents the build process from sending
119build process from sending the built files to the production web server. 117the built files to the production web server.
120 118
121#+begin_src sh 119#+begin_src sh
122#!/bin/bash 120#!/bin/bash
@@ -133,7 +131,6 @@ else
133fi 131fi
134#+end_src 132#+end_src
135 133
136You can modify the container in numerous ways and this approach allows 134You can modify the container in numerous ways and this approach allows you to
137you to create complex scenarios for your web development purposes. I 135create complex scenarios for your web development purposes. I highly recommend
138highly recommend switching over to a container-based approach for 136switching over to a container-based approach for simple, local web development.
139simple, local web development.
content/blog/2024-04-18-mu4e.org +36 −42
@@ -3,20 +3,18 @@
3#+description: 3#+description:
4#+slug: mu4e 4#+slug: mu4e
5 5
6This post was heavily inspired by 6This post was heavily inspired by [[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in Emacs with Mu4e on macOS]], but
7[[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in 7with my own tweaks for a single-account configuration and some Doom-specific
8Emacs with Mu4e on macOS]], but with my own tweaks for a single-account 8configurations.
9configuration and some Doom-specific configurations.
10 9
11* Overview 10* Overview
12 11
13[[https://github.com/emacsmirror/mu4e][Mu4e]] is an Emacs-based email 12[[https://github.com/emacsmirror/mu4e][Mu4e]] is an Emacs-based email client based on [[https://www.djcbsoftware.nl/code/mu/][mu]], an indexer that stores email in
14client based on [[https://www.djcbsoftware.nl/code/mu/][mu]], an indexer 13the Maildir format.
15that stores email in the Maildir format.
16 14
17This blog post covers the installation of Mu4e in Doom Emacs on macOS. 15This blog post covers the installation of Mu4e in Doom Emacs on macOS. This
18This guide should be very similar to GNU Emacs and Linux-based systems, 16guide should be very similar to GNU Emacs and Linux-based systems, with a few
19with a few tweaks required in the various configuration files. 17tweaks required in the various configuration files.
20 18
21* Installation 19* Installation
22 20
@@ -34,21 +32,21 @@ mkdir ~/.maildir/certificates # used to store system root certificates
34 32
35*** Store Email Account Passwords in macOS Keychain 33*** Store Email Account Passwords in macOS Keychain
36 34
37Next, I will be using the macOS Keychain to store my email account 35Next, I will be using the macOS Keychain to store my email account passwords
38passwords using the command below. 36using the command below.
39 37
40#+begin_src sh 38#+begin_src sh
41security add-generic-password -s mu4e-example -a you@example.com -w 39security add-generic-password -s mu4e-example -a you@example.com -w
42#+end_src 40#+end_src
43 41
44This will prompt you to input your password twice to confirm. Keep the 42This will prompt you to input your password twice to confirm. Keep the
45=mu4e-example= name in mind, as you will need to reference it later in 43=mu4e-example= name in mind, as you will need to reference it later in the IMAP
46the IMAP and SMTP configuration files. 44and SMTP configuration files.
47 45
48*** Store Root Certificates 46*** Store Root Certificates
49 47
50In order to use IMAP and SMTP, we need to provide certificates to the 48In order to use IMAP and SMTP, we need to provide certificates to the local
51local services. We will use the macOS defaults for this. 49services. We will use the macOS defaults for this.
52 50
531. Open =Keychain Access.app=. 511. Open =Keychain Access.app=.
542. Select =System Roots= in the sidebar. 522. Select =System Roots= in the sidebar.
@@ -58,8 +56,8 @@ local services. We will use the macOS defaults for this.
58 56
59*** Install Dependencies 57*** Install Dependencies
60 58
61Install =mbsync= (via =isync=) to fetch emails via IMAP, =mu= to index 59Install =mbsync= (via =isync=) to fetch emails via IMAP, =mu= to index emails,
62emails, and =msmtp= to send emails via SMTP. 60and =msmtp= to send emails via SMTP.
63 61
64#+begin_src sh 62#+begin_src sh
65brew install mu isync msmtp 63brew install mu isync msmtp
@@ -73,8 +71,8 @@ Within Doom Emacs, we can install Mu4e by enabling the package.
73nano ~/.doom.d/init.el 71nano ~/.doom.d/init.el
74#+end_src 72#+end_src
75 73
76In this file, uncomment the =mu4e= line within the =:email= section. You 74In this file, uncomment the =mu4e= line within the =:email= section. You can
77can also enable the =+org= and =+gmail= options if you prefer. 75also enable the =+org= and =+gmail= options if you prefer.
78 76
79#+begin_src lisp 77#+begin_src lisp
80(doom! :input 78(doom! :input
@@ -88,9 +86,9 @@ can also enable the =+org= and =+gmail= options if you prefer.
88 86
89* Configuration 87* Configuration
90 88
91As an overall suggestion, I create the following configuration files in 89As an overall suggestion, I create the following configuration files in the
92the =~/.maildir= directory and using symlinks to their proper locations 90=~/.maildir= directory and using symlinks to their proper locations so that I
93so that I can backup and restore these files easily. 91can backup and restore these files easily.
94 92
95#+begin_src sh 93#+begin_src sh
96touch ~/.maildir/.mbsyncrc && \ 94touch ~/.maildir/.mbsyncrc && \
@@ -99,14 +97,13 @@ ln -s /Users/username/.maildir/.mbsyncrc /Users/username/.mbsyncrc && \
99ln -s /Users/username/.maildir/.msmtprc /Users/username/.msmtprc 97ln -s /Users/username/.maildir/.msmtprc /Users/username/.msmtprc
100#+end_src 98#+end_src
101 99
102You can also create these files in your home directory and skip the 100You can also create these files in your home directory and skip the symlinking
103symlinking process above. 101process above.
104 102
105** IMAP 103** IMAP
106 104
107Next, let's configure =mbsync= in the file created above. Paste the 105Next, let's configure =mbsync= in the file created above. Paste the following
108following information and customize it to match your mail provider's 106information and customize it to match your mail provider's information.
109information.
110 107
111#+begin_src sh 108#+begin_src sh
112nano ~/.maildir/.mbsyncrc 109nano ~/.maildir/.mbsyncrc
@@ -142,9 +139,8 @@ SyncState *
142 139
143** SMTP 140** SMTP
144 141
145Next, let's configured =msmtprc= in the file created above. Paste the 142Next, let's configured =msmtprc= in the file created above. Paste the following
146following information and customize it to match your mail provider's 143information and customize it to match your mail provider's information.
147information.
148 144
149#+begin_src sh 145#+begin_src sh
150nano ~/.maildir/.msmtprc 146nano ~/.maildir/.msmtprc
@@ -176,8 +172,8 @@ account default : startmail
176 172
177** Doom Emacs 173** Doom Emacs
178 174
179Finally, we need to configure Doom Emacs to use the proper packages and 175Finally, we need to configure Doom Emacs to use the proper packages and set some
180set some variables and functions. 176variables and functions.
181 177
182#+begin_src sh 178#+begin_src sh
183nano ~/.doom.d/config.el 179nano ~/.doom.d/config.el
@@ -275,14 +271,12 @@ Be sure to sync Doom to update the current configurations.
275doom sync 271doom sync
276#+end_src 272#+end_src
277 273
278If you have Doom open, execute =SPC h r r= to reload the new 274If you have Doom open, execute =SPC h r r= to reload the new configurations.
279configurations.
280 275
281* Initial Sync 276* Initial Sync
282 277
283Once you have configured all of the relevant files, you can perform an 278Once you have configured all of the relevant files, you can perform an initial
284initial sync. Note that you can perform syncing within Mu4e itself after 279sync. Note that you can perform syncing within Mu4e itself after this.
285this.
286 280
287#+begin_src sh 281#+begin_src sh
288mbsync -aV 282mbsync -aV
@@ -299,11 +293,11 @@ The emails will now to be ready to use!
299 293
300* Screenshots 294* Screenshots
301 295
302You can now launch Doom and open Mu4e with =SPC o m=. You can also 296You can now launch Doom and open Mu4e with =SPC o m=. You can also explore the
303explore the Mu4e options with =SPC : mu4e=. 297Mu4e options with =SPC : mu4e=.
304 298
305The home page shows various options and metadata about the account 299The home page shows various options and metadata about the account you've
306you've opened. 300opened.
307 301
308#+caption: Mu4e Home Page 302#+caption: Mu4e Home Page
309[[https://img.cleberg.net/blog/20240418-mu4e/mu4e.png]] 303[[https://img.cleberg.net/blog/20240418-mu4e/mu4e.png]]
content/blog/2024-05-03-ubuntu-on-macos.org +38 −42
@@ -3,24 +3,24 @@
3#+description: Learn how to run Linux machines as applications on macOS with minimal hassle. 3#+description: Learn how to run Linux machines as applications on macOS with minimal hassle.
4#+slug: ubuntu-on-macos 4#+slug: ubuntu-on-macos
5 5
6Being a macOS user who previously used Linux for many years, I often 6Being a macOS user who previously used Linux for many years, I often find myself
7find myself searching for alternatives to the Linux-native tools and 7searching for alternatives to the Linux-native tools and methods that I had
8methods that I had become used to over the years. 8become used to over the years.
9 9
10Luckily, there's an option to simply use Linux directly on your macOS 10Luckily, there's an option to simply use Linux directly on your macOS device
11device without having to boot the whole device into Linux or having to 11without having to boot the whole device into Linux or having to SSH into another
12SSH into another Linux machine. 12Linux machine.
13 13
14In this post, I use [[https://orbstack.dev/][OrbStack]] to create an 14In this post, I use [[https://orbstack.dev/][OrbStack]] to create an Ubuntu Linux machine to take advantage
15Ubuntu Linux machine to take advantage of the Linux system. 15of the Linux system.
16 16
17** Installing OrbStack 17** Installing OrbStack
18 18
19OrbStack has a [[https://orbstack.dev/download][download page]] if you 19OrbStack has a [[https://orbstack.dev/download][download page]] if you prefer to use =*.dmg= files to install
20prefer to use =*.dmg= files to install applications. 20applications.
21 21
22Personally, I prefer to install everything through Homebrew. For 22Personally, I prefer to install everything through Homebrew. For OrbStack, run
23OrbStack, run the following to install the application via Homebrew: 23the following to install the application via Homebrew:
24 24
25#+begin_src sh 25#+begin_src sh
26brew install orbstack 26brew install orbstack
@@ -28,72 +28,68 @@ brew install orbstack
28 28
29** Creating a Linux Machine 29** Creating a Linux Machine
30 30
31Once installed, you can launch OrbStack and create a Linux machine in a 31Once installed, you can launch OrbStack and create a Linux machine in a matter
32matter of seconds. The creation dialog defaults to Ubuntu. 32of seconds. The creation dialog defaults to Ubuntu.
33 33
34#+caption: OrbStack's Linux Machine Creation Page 34#+caption: OrbStack's Linux Machine Creation Page
35[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/create.png]] 35[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/create.png]]
36 36
37** Launching the Machine 37** Launching the Machine
38 38
39Once created, the dialog will close and you will be presented with your 39Once created, the dialog will close and you will be presented with your new list
40new list of machines. Simply double-click on a machine to run it. 40of machines. Simply double-click on a machine to run it.
41 41
42When you run the machine for the first time, it may warn you that a 42When you run the machine for the first time, it may warn you that a program is
43program is attempting to run a shell script. You can choose to manually 43attempting to run a shell script. You can choose to manually inspect this shell
44inspect this shell script via the provided path if you wish. When you're 44script via the provided path if you wish. When you're comfortable with the
45comfortable with the script, you can toggle the 45script, you can toggle the =Suppress this message permanently= button and click
46=Suppress this message permanently= button and click OK to run the 46OK to run the machine.
47machine.
48 47
49#+begin_quote 48#+begin_quote
50*Quick Tip*! 49*Quick Tip*!
51 50
52You will always be able to launch the OrbStack application and control 51You will always be able to launch the OrbStack application and control the
53the machine via the Machines page, but you can also use the OrbStack 52machine via the Machines page, but you can also use the OrbStack icon in the
54icon in the macOS menu bar to quickly control the machine without 53macOS menu bar to quickly control the machine without launching OrbStack itself.
55launching OrbStack itself.
56#+end_quote 54#+end_quote
57 55
58** Working in Linux 56** Working in Linux
59 57
60Once successful, OrbStack will launch your default terminal and log you 58Once successful, OrbStack will launch your default terminal and log you into the
61into the Linux machine you created above. 59Linux machine you created above.
62 60
63In the example below, we can see that the machine is using 61In the example below, we can see that the machine is using =Linux ubuntu 6.9.6=
64=Linux ubuntu 6.9.6= and operates similar to a fresh Ubuntu install on 62and operates similar to a fresh Ubuntu install on bare metal.
65bare metal.
66 63
67#+caption: Ubuntu CLI 64#+caption: Ubuntu CLI
68[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/cli.png]] 65[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/cli.png]]
69 66
70You can also click the machine's folder icon in OrbStack to open the 67You can also click the machine's folder icon in OrbStack to open the machine's
71machine's file system within macOS's native Files app. Within Files, you 68file system within macOS's native Files app. Within Files, you can double-click
72can double-click to open any files and they will open in the macOS 69to open any files and they will open in the macOS defaults for those file types
73defaults for those file types (e.g., TextEdit). 70(e.g., TextEdit).
74 71
75#+caption: Ubuntu Files 72#+caption: Ubuntu Files
76[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/files.png]] 73[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/files.png]]
77 74
78Here's an example of opening the =test.md= file in the screenshot above, 75Here's an example of opening the =test.md= file in the screenshot above, which
79which defaults to opening in Xcode on my system. 76defaults to opening in Xcode on my system.
80 77
81#+caption: Ubuntu Files 78#+caption: Ubuntu Files
82[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/xcode.png]] 79[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/xcode.png]]
83 80
84*** Test Examples 81*** Test Examples
85 82
86As a quick example of the system, I installed =neofetch=, switched to 83As a quick example of the system, I installed =neofetch=, switched to the =zsh=
87the =zsh= shell, and ran a few common commands. 84shell, and ran a few common commands.
88 85
89#+caption: Test Examples 86#+caption: Test Examples
90[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/test.png]] 87[[https://img.cleberg.net/blog/20240503-ubuntu-on-macos/test.png]]
91 88
92** Thoughts 89** Thoughts
93 90
94The installation, creation, and launch processes are seamless and 91The installation, creation, and launch processes are seamless and provide an
95provide an easy way to test or use Linux on macOS with little to no 92easy way to test or use Linux on macOS with little to no hassle.
96hassle.
97 93
98The shell and file integrations are incredibly snappy and I have not 94The shell and file integrations are incredibly snappy and I have not
99experienced any lag so far. Further, being able to open files in the 95experienced any lag so far. Further, being able to open files in the
content/blog/2024-06-19-deprecated-trusted-gpg-fix.org +33 −35
@@ -5,8 +5,8 @@
5 5
6** System Warning 6** System Warning
7 7
8When running an update on an Ubuntu system, you may have run into a 8When running an update on an Ubuntu system, you may have run into a system
9system warning that looks like the example below. 9warning that looks like the example below.
10 10
11#+begin_src txt 11#+begin_src txt
12W: https://dl.yarnpkg.com/debian/dists/stable/InRelease: Key is stored in legacy 12W: https://dl.yarnpkg.com/debian/dists/stable/InRelease: Key is stored in legacy
@@ -14,34 +14,33 @@ trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in
14apt-key(8) for details. 14apt-key(8) for details.
15#+end_src 15#+end_src
16 16
17While this example references the =yarn= package, the warning message is 17While this example references the =yarn= package, the warning message is the
18the same for any repository using the deprecated =trusted.gpg= key ring. 18same for any repository using the deprecated =trusted.gpg= key ring.
19 19
20The issue arises from managing keys with the =apt-key= command, which 20The issue arises from managing keys with the =apt-key= command, which utilizes
21utilizes the =/etc/apt/trusted.gpg= file by default. Instead, Ubuntu has 21the =/etc/apt/trusted.gpg= file by default. Instead, Ubuntu has moved to
22moved to managing key rings with individual =.gpg= files in the 22managing key rings with individual =.gpg= files in the =/etc/apt/trusted.gpg.d/=
23=/etc/apt/trusted.gpg.d/= directory. 23directory.
24 24
25To fix this issue, let's check to see which keys are using the 25To fix this issue, let's check to see which keys are using the =trusted.gpg= key
26=trusted.gpg= key ring and move them into their own dedicated key ring. 26ring and move them into their own dedicated key ring.
27 27
28** Finding All Keys in the Keyring 28** Finding All Keys in the Keyring
29 29
30Let's start by simply listing the keys used by the =apt= commands. To do 30Let's start by simply listing the keys used by the =apt= commands. To do this,
31this, run the following command. 31run the following command.
32 32
33#+begin_src sh 33#+begin_src sh
34sudo apt-key list 34sudo apt-key list
35#+end_src 35#+end_src
36 36
37This command will show an output similar to the one below. You may see 37This command will show an output similar to the one below. You may see
38additional keys in the =/etc/apt/trusted.gpg.d/= directory - this is 38additional keys in the =/etc/apt/trusted.gpg.d/= directory - this is where we
39where we will be moving any keys currently found in the =trusted.gpg= 39will be moving any keys currently found in the =trusted.gpg= key ring.
40key ring.
41 40
42In the below example, we can see that this system has four different GPG 41In the below example, we can see that this system has four different GPG keys
43keys stored within the =trusted.gpg= key ring. Let's go ahead and move 42stored within the =trusted.gpg= key ring. Let's go ahead and move them into
44them into their own files. 43their own files.
45 44
46#+begin_src txt 45#+begin_src txt
47Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead 46Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead
@@ -73,24 +72,24 @@ uid [ unknown] nginx signing key <signing-key-3@nginx.com>
73 72
74*** Exporting Keys to New Files 73*** Exporting Keys to New Files
75 74
76Now that we know the keys, we will need to move them into their own key 75Now that we know the keys, we will need to move them into their own key ring. We
77ring. We can do this by copying the last eight (8) characters from the 76can do this by copying the last eight (8) characters from the key's signature
78key's signature and exporting it from this key ring into its own. 77and exporting it from this key ring into its own.
79 78
80Using the yarn example from the beginning, here's the command to move 79Using the yarn example from the beginning, here's the command to move this key
81this key into its own key ring. 80into its own key ring.
82 81
83#+begin_src sh 82#+begin_src sh
84sudo apt-key export 86E50310 | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/yarn.gpg 83sudo apt-key export 86E50310 | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/yarn.gpg
85#+end_src 84#+end_src
86 85
87You can repeat this process for any other keys, such as the =nginx= keys 86You can repeat this process for any other keys, such as the =nginx= keys in the
88in the example above. 87example above.
89 88
90*** Cleaning Up 89*** Cleaning Up
91 90
92If you run =sudo apt-key list= again, you should see the keys within 91If you run =sudo apt-key list= again, you should see the keys within their own
93their own key rings: 92key rings:
94 93
95#+begin_src txt 94#+begin_src txt
96/etc/apt/trusted.gpg.d/nginx-archive-keyring.gpg 95/etc/apt/trusted.gpg.d/nginx-archive-keyring.gpg
@@ -117,17 +116,16 @@ sub rsa4096 2019-01-02 [S] [expires: 2026-01-23]
117sub rsa4096 2019-01-11 [S] [expires: 2026-01-23] 116sub rsa4096 2019-01-11 [S] [expires: 2026-01-23]
118#+end_src 117#+end_src
119 118
120Once you have verified that the keys are valid and stored in their own 119Once you have verified that the keys are valid and stored in their own key
121key rings, you can archive the =trusted.gpg= file and run a system 120rings, you can archive the =trusted.gpg= file and run a system update to test
122update to test the new files. 121the new files.
123 122
124#+begin_src sh 123#+begin_src sh
125sudo mv /etc/apt/trusted.gpg /etc/apt/trusted.gpg.bkp 124sudo mv /etc/apt/trusted.gpg /etc/apt/trusted.gpg.bkp
126sudo apt update 125sudo apt update
127#+end_src 126#+end_src
128 127
129Once you've verified that updates work as expected and that the keys are 128Once you've verified that updates work as expected and that the keys are working
130working as intended, you can delete the =.bkp= file created above. If 129as intended, you can delete the =.bkp= file created above. If you're storing
131you're storing keys that are not easily re-attainable, I suggest keeping 130keys that are not easily re-attainable, I suggest keeping the =.bkp= file stored
132the =.bkp= file stored in a safe location until you are positive that 131in a safe location until you are positive that you no longer need it.
133you no longer need it.
content/blog/2024-07-11-emacs-on-ipad.org +24 −28
@@ -5,25 +5,23 @@
5#+slug: emacs-on-ipad 5#+slug: emacs-on-ipad
6 6
7 7
8This post describes the process to install and use Emacs on the iPad Air 8This post describes the process to install and use Emacs on the iPad Air 13-inch
913-inch (M2). The iPad used in this post is running iPadOS 17.6. 9(M2). The iPad used in this post is running iPadOS 17.6.
10 10
11** Shell Application 11** Shell Application
12 12
13In order to use Emacs on an iPad, you will need a terminal emulator 13In order to use Emacs on an iPad, you will need a terminal emulator application.
14application. I recommend 14I recommend [[https://apps.apple.com/us/app/ish-shell/id1436902243][iSH]], since it runs a version of Alpine Linux within the app itself
15[[https://apps.apple.com/us/app/ish-shell/id1436902243][iSH]], since it 15and will allow you to install packages that you need.
16runs a version of Alpine Linux within the app itself and will allow you
17to install packages that you need.
18 16
19#+caption: iSH Application 17#+caption: iSH Application
20[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/ish.png]] 18[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/ish.png]]
21 19
22** Require Packages 20** Require Packages
23 21
24I started by adding the required packages directly within iSH. Emacs 22I started by adding the required packages directly within iSH. Emacs should
25should install dependencies by default, but I include a few other 23install dependencies by default, but I include a few other packages that I use
26packages that I use in my terminal as well. 24in my terminal as well.
27 25
28#+begin_src sh 26#+begin_src sh
29apk add emacs ripgrep fd findutils 27apk add emacs ripgrep fd findutils
@@ -34,39 +32,37 @@ apk add emacs ripgrep fd findutils
34 32
35** Emacs 33** Emacs
36 34
37Once this is complete, you should be able to run Emacs natively on your 35Once this is complete, you should be able to run Emacs natively on your iPad.
38iPad. It's effective, but can be slow at times. 36It's effective, but can be slow at times.
39 37
40I attempted to also install Doom Emacs, which technically worked, but 38I attempted to also install Doom Emacs, which technically worked, but was so
41was so incredibly slow and buggy that I was not even able to take 39incredibly slow and buggy that I was not even able to take screenshots. Someone
42screenshots. Someone smarter than me could likely get it to work with a 40smarter than me could likely get it to work with a little tinkering.
43little tinkering.
44 41
45#+caption: Emacs 42#+caption: Emacs
46[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/emacs.png]] 43[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/emacs.png]]
47 44
48*** MELPA 45*** MELPA
49 46
50You also have to remember to hook up MELPA yourself in the =.emacs= file 47You also have to remember to hook up MELPA yourself in the =.emacs= file to be
51to be able to search through their 5700+ packages instead of just ELPA 48able to search through their 5700+ packages instead of just ELPA packages. If
52packages. If you don't, you will only have access to ELPA packages like 49you don't, you will only have access to ELPA packages like the ones below.
53the ones below.
54 50
55#+caption: package-install 51#+caption: package-install
56[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/melpa.png]] 52[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/melpa.png]]
57 53
58Once you have MELPA, you can install packages like the =dashboard= 54Once you have MELPA, you can install packages like the =dashboard= package shown
59package shown below. 55below.
60 56
61#+caption: emacs-dashboard 57#+caption: emacs-dashboard
62[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/dashboard.png]] 58[[https://img.cleberg.net/blog/20240711-emacs-on-ipad/dashboard.png]]
63 59
64*** Speed 60*** Speed
65 61
66While Emacs will run on my iPad, it's not perfect. The largest issue on 62While Emacs will run on my iPad, it's not perfect. The largest issue on my iPad
67my iPad is speed - loading Emacs takes 6-7 seconds and installing the 63is speed - loading Emacs takes 6-7 seconds and installing the =magit= package
68=magit= package took 129 seconds. 64took 129 seconds.
69 65
70I haven't played around enough to optimize loading times and poke around 66I haven't played around enough to optimize loading times and poke around to see
71to see why the network requests take so long, but it's a big enough 67why the network requests take so long, but it's a big enough issue that I
72issue that I wouldn't see casual Emacs users dealing with the lag. 68wouldn't see casual Emacs users dealing with the lag.