diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index b43b2db..5b96165 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -11,10 +11,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - uses: cachix/install-nix-action@v6 - - uses: cachix/cachix-action@v3 + - uses: cachix/install-nix-action@v12 + - uses: cachix/cachix-action@v7 with: name: xe + - run: nix-build - name: Log into GitHub Container Registry run: echo "${{ secrets.CR_PAT }}" | docker login https://ghcr.io -u ${{ github.actor }} --password-stdin - name: Docker push diff --git a/blog/various-updates-2020-11-18.markdown b/blog/various-updates-2020-11-18.markdown new file mode 100644 index 0000000..bc85ebc --- /dev/null +++ b/blog/various-updates-2020-11-18.markdown @@ -0,0 +1,230 @@ +--- +title: Various Updates +date: 2020-11-18 +tags: + - personal + - consulting + - docker + - nixos +--- + +# Various Updates + +Immigration purgatory is an experience. It's got a lot of waiting and there is a +lot of uncertainty that can make it feel stressful. Like I said +[before](/blog/new-adventures-2020-10-24), I'm not concerned; however I have a +lot of free time on my hands and I've been using it to make some plans for the +blog (and a new offering for companies that need help dealing with the new +[Docker Hub rate +limits](https://docs.docker.com/docker-hub/download-rate-limit/)) in the future. +I'm gonna outline them below in their own sections. This blogpost was originally +about 4 separate blogposts that I started and abandoned because I had trouble +focusing on finishing them. Stress sucks lol. + +## WebMention Support + +I recently deployed [mi v1.0.0](https://github.com/Xe/mi) to my home cluster. mi +is a service that handles a lot of personal API tasks including the automatic +post notifications to Twitter and Mastodon. The old implementation was in Go and +stored its data in RethinkDB. I also have a snazzy frontend in Elm for mi. This +new version is rewritten from scratch to use Rust, [Rocket](https://rocket.rs/) +and SQLite. It is also fully +[nixified](https://github.com/Xe/mi/blob/mara/default.nix) and is deployed to my +home cluster via a [NixOS +module](https://github.com/Xe/nixos-configs/blob/master/common/services/mi.nix). + +One of the major new features I have in this rewrite is +[WebMention](https://www.w3.org/TR/webmention/) support. WebMentions allow +compatible websites to "mention" my articles or other pages on my main domains +by sending a specially formatted HTTP request to mi. I am still in the early +stages of integrating mi into my site code, but eventually I hope to have a list +of places that articles are mentioned in each post. The WebMention endpoint for +my site is `https://mi.within.website/api/webmention/accept`. I have added +WebMention metadata into the HTML source of the blog pages as well as in the +`Link` header as the W3 spec demands. + +If you encounter any issues with this feature, please [let me know](/contact) so +I can get it fixed as soon as possible. + +### Thoughts on Elm as Used in mi + +[Elm](https://elm-lang.org/) is an interesting language for making single page +applications. The old version of mi was the first time I had really ever used +Elm for anything serious and after some research I settled on using +[elm-spa](https://www.elm-spa.dev/) as a framework to smooth over some of the +weirder parts of the language. elm-spa worked great at first. All of the pages +were separated out into their own components and the routing setup was really +intuitive (if a bit weird because of the magic involved). It's worked great for +a few years and has been very low maintenance. + +However when I was starting to implement the backend of mi in Rust, I tried to +nixify the elm-spa frontend I made. This was a disaster. The magic that elm-spa +relied on fell apart and _at the time I attempted to do this_ it was very +difficult to do this. + +As a result I ended up rewriting the frontend in very very boring Elm using +information from the [Elm Guide](https://guide.elm-lang.org/) and a lot of +blogposts and help from the Elm slack. Overall this was a successful experiment +and I can easily see this new frontend (which I have named sina as a compound +[toki pona](https://tokipona.org/) pun) becoming a powerful tool for +investigating and managing the data in mi. + +[Special thanks to malinoff, wolfadex, chadtech and mfeineis on the Elm slack +for helping with the weird issues involved in getting a split model approach +working.](conversation://Mara/hacker) + +Feel free to check out the code [here](https://github.com/Xe/mi/tree/mara/sina). +I may try to make an Elm frontend to my site for people that use the Progressive +Web App support. + +### elm2nix + +[elm2nix](https://github.com/cachix/elm2nix) is a very nice tool that lets you +generate Nix definitions from Elm packages, however the +template it uses is a bit out of date. To fix it you need to do the following: + +```console +$ elm2nix init > default.nix +$ elm2nix convert > elm-srcs.nix +$ elm2nix snapshot +``` + +Then open `default.nix` in your favorite text editor and change this: + +```nix + buildInputs = [ elmPackages.elm ] + ++ lib.optional outputJavaScript nodePackages_10_x.uglify-js; +``` + +to this: + +```nix + buildInputs = [ elmPackages.elm ] + ++ lib.optional outputJavaScript nodePackages.uglify-js; +``` + +and this: + +```nix + uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \ + | uglifyjs --mangle --output=$out/${module}.min.${extension} +``` + +to this: + +```nix + uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \ + | uglifyjs --mangle --output $out/${module}.min.${extension} +``` + +These issues should be fixed in the next release of elm2nix. + +## New Character in the Blog Cutouts + +As I mentioned [in the past](/blog/how-mara-works-2020-09-30), I am looking into +developing out other characters for my blog. I am still in the early stages of +designing this, but I think the next character in my blog is going to be an +anthro snow leopard named Alicia. I want Alicia to be a beginner that is very +new to computer programming and other topics, which would then make Mara into +more of a teacher type. I may also introduce my own OC Cadey (the orca looking +thing you can see [here](https://christine.website/static/img/avatar_large.png) +or in the favicon of my site) into the mix to reply to these questions in +something more close to the Socratic method. + +Some people have joked that the introduction of Mara turned my blog into a shark +visual novel that teaches you things. This sounds hilarious to me, and I am +looking into what it would take to make an actual visual novel on a page on my +blog using Rust and WebAssembly. I am in very early planning stages for this, so +don't expect this to come out any time soon. + +## Gergoplex Build + +My [Gergoplex kit](https://www.gboards.ca/product/gergoplex) finally came in +yesterday, and I got to work soldering it up with some switches and applying the +keycaps. + +![Me soldering the Gergoplex](https://cdn.christine.website/file/christine-static/img/keeb/gergoplex/EnEYNxvW4AEfWcH.jpg) + +![A glory shot of the Gergoplex](https://cdn.christine.website/file/christine-static/img/keeb/gergoplex/Elm3dN8XUAAYHws.jpg) + +I picked the Pro Red linear switches with a 35 gram spring in them (read: they +need 35 grams of force to actuate, which is lighter than most switches) and +typing on it is buttery smooth. The keycaps are a boring black, but they look +nice on it. + +Overall this kit (with the partial board, switches and keycaps) cost me about +US$124 (not including shipping) with the costs looking something like this: + +| Name | Count | Cost | +| :------------------------- | :----- | :---- | +| Gergoplex Partial Kit | 1 | $70 | +| Choc Pro Red 35g switches | 4 | $10 | +| Keycaps (15) | 3 | $30 | +| Braided interconnect cable | 1 | $7 | +| Mini-USB cable | 1 | $7 | + +I'd say this was a worthwhile experience. I haven't really soldered anything +since I was in high school and it was fun to pick up the iron again and make +something useful. If you are looking for a beginner soldering project, I can't +recommend the Gergoplex enough. + +I also picked up some extra switches and keycaps (prices not listed here) for a +future project involving an eInk display. More on that when it is time. + +## Branch Conventions + +You may have noticed that some of my projects have default branches named `main` +and others have default branches named `mara`. This difference is very +intentional. Repos with the default branch `main` generally contain code that is +"stable" and contains robust and reusable code. Repos with the default branch +`mara` are generally my experimental repos and the code in them may not be the +most reusable across other projects. mi is a repo with a `mara` default branch +because it is a very experimental thing. In the future I may promote it up to +having a `main` branch, however for now it's less effort to keep things the way +it is. + +## Docker Consulting + +The new [Docker Hub rate +limits](https://docs.docker.com/docker-hub/download-rate-limit/) have thrown a +wrench into many CI/CD setups as well as uncertainty in how CI services will +handle this. Many build pipelines implictly trust the Docker Hub to be up and +that it will serve the appropriate image so that your build can work. Many +organizations use their own Docker registry (GHCR, AWS/Google Cloud image +registries, Artifactory, etc.), however most image build definitions I've seen +start out with something like this: + +```Dockerfile +FROM golang:alpine +``` + +which will implicitly pull from the Docker Hub. This can lead to bad things. + +If you would like to have a call with me for examining your process for building +Docker images in CI and get a list of actionable suggestions for how to work +around this, [contact me](/contact) so that we can discuss pricing and +scheduling. + +I have been using Docker for my entire professional career (way back since +Docker required you to recompile your kernel to enable cgroup support in public +beta) and I can also discuss methods to make your Docker images as small as they +can possibly get. My record smallest Docker image is 5 MB. + +If either of these prospects interest you, please contact me so we can work +something out. + +--- + +Here's hoping that the immigration purgatory ends soon. I'm lucky enough to have +enough cash built up that I can weather this jobless month. I've been using this +time to work on personal projects (like mi and +[wasmcloud](https://wasmcloud.app)) and better myself. I've also done a little +writing that I plan to release in the future after I clean it up. + +In retrospect I probably should have done [NaNoWriMo](https://nanowrimo.org/) +seeing that I basically will have the entire month of November jobless. I've had +an idea for a while about someone that goes down the rabbit hole of mysticism +and magick, but I may end up incorporating that into the visual novel project I +mentioned in the Elm section. + +Be well and stay safe out there. Wear a mask, stay at home. diff --git a/config.dhall b/config.dhall index f461134..192955d 100644 --- a/config.dhall +++ b/config.dhall @@ -1,24 +1,30 @@ let Person = { Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text } , default = - { name = "", tags = [] : List Text, gitLink = "", twitter = "" } + { name = "", tags = [] : List Text, gitLink = "", twitter = "" } } let defaultPort = env:PORT ? 3030 +let defaultWebMentionEndpoint = + env:WEBMENTION_ENDPOINT + ? "https://mi.within.website/api/webmention/accept" + let Config = { Type = { signalboost : List Person.Type , port : Natural , clackSet : List Text , resumeFname : Text + , webMentionEndpoint : Text } , default = - { signalboost = [] : List Person.Type - , port = defaultPort - , clackSet = [ "Ashlynn" ] - , resumeFname = "./static/resume/resume.md" - } + { signalboost = [] : List Person.Type + , port = defaultPort + , clackSet = [ "Ashlynn" ] + , resumeFname = "./static/resume/resume.md" + , webMentionEndpoint = defaultWebMentionEndpoint + } } in Config::{ diff --git a/scripts/release.sh b/scripts/release.sh index 544df55..9510d39 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -7,4 +7,4 @@ kubectl rollout status -n apps deployment/christinewebsite kubectl apply -f ./k8s/job.yml sleep 10 kubectl delete -f ./k8s/job.yml -curl --http1.1 -H "Authorization: $MI_TOKEN" --data "https://christine.website/blog.json" https://mi.within.website/blog/refresh +curl --http1.1 -H "Authorization: $MI_TOKEN" https://mi.within.website/api/blog/refresh diff --git a/src/app/mod.rs b/src/app/mod.rs index 035db07..c18e121 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -2,24 +2,28 @@ use crate::{post::Post, signalboost::Person}; use color_eyre::eyre::Result; use serde::Deserialize; use std::{fs, path::PathBuf}; -use tracing::{instrument, error}; +use tracing::{error, instrument}; pub mod markdown; #[derive(Clone, Deserialize)] pub struct Config { #[serde(rename = "clackSet")] - clack_set: Vec, - signalboost: Vec, - port: u16, + pub(crate) clack_set: Vec, + pub(crate) signalboost: Vec, + pub(crate) port: u16, #[serde(rename = "resumeFname")] - resume_fname: PathBuf, + pub(crate) resume_fname: PathBuf, + #[serde(rename = "webMentionEndpoint")] + pub(crate) webmention_url: String, } #[instrument] async fn patrons() -> Result> { use patreon::*; - let creds: Credentials = envy::prefixed("PATREON_").from_env().unwrap_or(Credentials::default()); + let creds: Credentials = envy::prefixed("PATREON_") + .from_env() + .unwrap_or(Credentials::default()); let cli = Client::new(creds); match cli.campaign().await { diff --git a/src/main.rs b/src/main.rs index 01333c9..0baaeef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,12 +26,15 @@ async fn main() -> Result<()> { tracing_subscriber::fmt::init(); log::info!("starting up commit {}", env!("GITHUB_SHA")); - let state = Arc::new(app::init( - std::env::var("CONFIG_FNAME") - .unwrap_or("./config.dhall".into()) - .as_str() - .into(), - ).await?); + let state = Arc::new( + app::init( + std::env::var("CONFIG_FNAME") + .unwrap_or("./config.dhall".into()) + .as_str() + .into(), + ) + .await?, + ); let healthcheck = warp::get().and(warp::path(".within").and(warp::path("health")).map(|| "OK")); @@ -144,6 +147,17 @@ async fn main() -> Result<()> { ) }) .map(|reply| warp::reply::with_header(reply, "X-Clacks-Overhead", "GNU Ashlynn")) + .map(|reply| { + warp::reply::with_header( + reply, + "Link", + format!( + r#"<{}>; rel="webmention""#, + std::env::var("WEBMENTION_URL") + .unwrap_or("https://mi.within.website/api/webmention/accept".to_string()) + ), + ) + }) .with(warp::log(APPLICATION_NAME)) .recover(handlers::rejection); diff --git a/templates/header.rs.html b/templates/header.rs.html index 37e6eb2..ca49a36 100644 --- a/templates/header.rs.html +++ b/templates/header.rs.html @@ -30,7 +30,7 @@ - + @@ -38,6 +38,7 @@ + @if styles.is_some() {