Compare commits

...

21 Commits

Author SHA1 Message Date
Cadey Ratio f590fc71d1 fix devshell
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-22 23:39:08 +00:00
Cadey Ratio 2e539512b7 convert to flakes
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-22 23:36:25 +00:00
Cadey Ratio f51752ed3c stop using politics as a cudgel to discourage experimentation
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-21 12:53:15 +00:00
dependabot[bot] e825b1b904
build(deps): bump tracing-subscriber from 0.3.9 to 0.3.11 (#454)
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.9 to 0.3.11.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.9...tracing-subscriber-0.3.11)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-13 07:51:14 -04:00
Cadey Ratio 20aeb35890 css: fix conversation width in the HTML element
> Add min-width:0 to .conversation-chat in shim.css. This is not only a
> fix, but the correct fix.
>
> But WTF?
>
> Well quite simply, the default value of min-width is auto, which
> normally acts like 0, but for a flex item, auto becomes something more
> along the lines of min-content (except special complicated rules that I
> don't fully understand apply). The net result is that the minimum width
> prevents the code boxes in the conversation from using their overflow:
> auto to enable scrolling. Setting min-width: 0 is effectively just
> turning this special rule off, and causing it use more normal box sizing
> rules.
>
> I hate how weird and impossible to understand the css box model is.

Signed-off-by: Xe Iaso <me@christine.website>
2022-04-07 17:53:12 +00:00
Cadey Ratio 449ddabce1
Nix flakes 3 (#453)
* blog: add third nix flakes post

Signed-off-by: Xe <me@christine.website>

* make nix flakes post 3 better, thanks open

Signed-off-by: Xe Iaso <me@christine.website>
2022-04-06 21:43:45 -04:00
Cadey Ratio 6b771b5503 fix better????
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-05 21:02:14 -04:00
Cadey Ratio ea8e1e045a blow up without patrone
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-05 20:59:35 -04:00
Cadey Ratio 3a4827c887 log refresh token errors
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-05 20:53:47 -04:00
Cadey Ratio fd6ac469a6 fix???
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-05 20:40:18 -04:00
Cadey Ratio fa2ada9747 don't read patreon creds from envvars
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-05 20:35:07 -04:00
Cadey Ratio 3a5c7adc42 tolerate no patrone creds
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-06 00:01:52 +00:00
Cadey Ratio e5ee825c0a signal boost: make links optional
Closes #326
Closes #209

Signed-off-by: Xe <me@christine.website>
2022-04-02 16:47:54 +00:00
Cadey Ratio e665412345 blog/backslash-kubernetes: change title
Closes #394

Signed-off-by: Xe Iaso <me@christine.website>
2022-04-02 16:36:41 +00:00
kjain 828a5f277e
add kjain (#437) 2022-04-02 12:25:48 -04:00
Cadey Ratio 7c90296bf0 Update to Axum 0.5
Closes #446
Closes #447
Closes #448

Signed-off-by: Xe <me@christine.website>
2022-04-02 16:24:39 +00:00
Cadey Ratio 1c8c3396a7 lib/patreon: refresh token support
This should hopefully make the patrons page work consistently and no
longer require me to manually update the patreon token once per month.
Why didn't I do this age ago??????

Hacked up live on twitch: https://twitch.tv/princessxen

Closes #442

Signed-off-by: Xe <me@christine.website>
2022-04-02 16:15:10 +00:00
Cadey Ratio 0c0c5875e6 oops lol
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-01 20:34:25 -04:00
Cadey Ratio 7fdae76543 blog: suggestions for recruiters
Signed-off-by: Xe Iaso <me@christine.website>
2022-04-01 19:44:34 -04:00
Cadey Ratio 1bedcb6a25 compiling matter in my living room
Signed-off-by: Christine Dodrill <me@christine.website>
2022-03-28 18:47:50 -04:00
Martin Schwaighofer 66574582f2
nix flakes can access private repos with git+ssh (#444)
Sadly using ssh+git does not work. I tested this locally.
See also: https://discourse.nixos.org/t/url-format-for-flake-over-git-ssh/7538/2
2022-03-26 14:30:54 -04:00
24 changed files with 1199 additions and 135 deletions

2
.envrc
View File

@ -1 +1 @@
eval "$(lorri direnv)"
use flake

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ cw.tar
/result
.#*
/target
.patreon.json
.direnv

26
Cargo.lock generated
View File

@ -130,9 +130,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.4.8"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9f346c92c1e9a71d14fe4aaf7c2a5d9932cc4e5e48d8fb6641524416eb79ddd"
checksum = "5611d4977882c5af1c0f7a34d51b5d87f784f86912bb543986b014ea4995ef93"
dependencies = [
"async-trait",
"axum-core",
@ -142,6 +142,7 @@ dependencies = [
"http",
"http-body",
"hyper",
"itoa",
"matchit",
"memchr",
"mime",
@ -160,9 +161,9 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbcda393bef9c87572779cb8ef916f12d77750b27535dd6819fa86591627a51"
checksum = "95cd109b3e93c9541dcce5b0219dcf89169dcc58c1bebed65082808324258afb"
dependencies = [
"async-trait",
"bytes",
@ -174,9 +175,9 @@ dependencies = [
[[package]]
name = "axum-extra"
version = "0.1.5"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b6d79bc9c2975821d39c7df31ea766026beb9efe28c076a48cfd7d50f34f18"
checksum = "ff3819ded1be91d7ee2cd9f0466aa345cc70ba0b0035ed47e3eac6427f83b81a"
dependencies = [
"axum",
"bytes",
@ -191,9 +192,9 @@ dependencies = [
[[package]]
name = "axum-macros"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5b2a9133b2658e684c8ea04157a8bd48dac7906a2eb884ffebfb051af123394"
checksum = "63bcb0d395bc5dd286e61aada9fc48201eb70e232f006f9d6c330c9db2f256f5"
dependencies = [
"heck",
"proc-macro2",
@ -1190,9 +1191,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "matchit"
version = "0.4.6"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833"
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
[[package]]
name = "md5"
@ -1514,6 +1515,7 @@ dependencies = [
"tokio",
"tracing",
"tracing-futures",
"url",
]
[[package]]
@ -2467,9 +2469,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.9"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
dependencies = [
"ansi_term",
"sharded-slab",

View File

@ -9,9 +9,9 @@ repository = "https://github.com/Xe/site"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.4"
axum-macros = "0.1"
axum-extra = "0.1"
axum = "0.5"
axum-macros = "0.2"
axum-extra = "0.2"
color-eyre = "0.6"
chrono = "0.4"
comrak = "0.12.1"

View File

@ -0,0 +1,93 @@
---
title: Compiling Code to Matter in My Living Room
date: 2022-03-28
tags:
- openscad
- 3dprinting
---
In a moment of weakness, my husband and I got a 3d printer. It's mostly been sitting around and not doing much since we got it, but recently I found a great use for it: I wanted a controller stand for my Valve Index controllers and VR full body trackers.
After doing some digging on Thingiverse, I found [this stand](https://www.thingiverse.com/thing:4587097) that looked like it had promise. So I downloaded the model, sliced it and then sent it over to Kyubey:
<blockquote class="twitter-tweet"><p lang="tl" dir="ltr">Kyuubey is happy <a href="https://t.co/atTLN8MSgc">pic.twitter.com/atTLN8MSgc</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1507485129907871747?ref_src=twsrc%5Etfw">March 25, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[Kyubey's name is a reference to <a href="https://madoka.fandom.com/wiki/Kyubey">Kyubey</a> from Puella Magi Madoka Magika</a>.](conversation://Mara/hacker)
Once it was done I ended up with a stand that I could feed [these cables I got from Amazon](https://www.amazon.ca/gp/product/B09LSF8XL9/) through. The tracker holes worked great, but the controller holes were just barely too small.
This was kinda frustrating and I almost gave up on the project, but then I remembered that [OpenSCAD](https://openscad.org) existed. OpenSCAD is a weird programming environment / 3D modeling hybrid program that I've seen used on Thingiverse. It works by letting you position platonic solids into a 3d environment, and from there you can create anything you want.
One of the primitives that OpenSCAD offers is a cylinder. So I wondered if I could use one of those to widen the hole in the index stand and then reprint the part with the wider hole.
[Wait, you're using a CAD program to fix your 3D print by modifying the model instead of using, I don't know, a drill and 5 minutes to make it fit that way?](conversation://Numa/dismay)
[There's no doing like overdoing!](conversation://Cadey/enby)
After some finangling, I managed to get the cylinders in the right place with this OpenSCAD code:
```scad
//difference() {
color("magenta") translate([0, 0, 0]) import("./assets/ValveTrackerDeckEditedByInugoro.stl");
// bores for controller holders
color([0, 1, 0]) translate([63, 44, 0]) cylinder(h = 55, r = 4.75);
color([0, 1, 0]) translate([-63, 44, 0]) cylinder(h = 55, r = 4.75);
//}
```
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Some finagling required <a href="https://t.co/7T0R6x1XoP">pic.twitter.com/7T0R6x1XoP</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508566854926745614?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
And when I uncommented out the `difference()` block, it ends up looking good enough:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/fiShvlN8QH">pic.twitter.com/fiShvlN8QH</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508567556759728141?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
So then I took a good solid look at the rest of the 3D printed part to see if I could improve on anything else before I sent it to another round of the printer. The last stand took _14 hours_ to print and used a lot of material. I want to avoid waste.
Something I noticed is that the front of the print where all the cables come out was a bit too thin. All 5 of the cables wouldn't fit in there (my braided cables must have been thicker than the ones that the original modeler used). So again I grabbed a few platonic solids and managed to make it work out:
```scad
// widen the paths
color("green") translate([0, -16, 1.3]) rotate([0, 0, 90]) cube([10, 57, 7.8], center = true);
color("green") translate([0, 0, 1.7]) rotate([0, 0, 0]) cube([25, 30, 7], center = true);
```
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/pKAVtiPfDS">pic.twitter.com/pKAVtiPfDS</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508568858650685440?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Then I wanted to add some wedges into the underside of the part to help me get the print off the bed. Most people have a problem with bed adhesion being too little. I have too much bed adhesion. So I added some angled rectangles:
```scad
// wedges to help get the print off the bed
color([1, 1, 0]) translate([-120, 0, 0]) rotate([15, 0, 90]) cube([10, 11, 2], center = true); // right
color([1, 1, 0]) translate([120, 0, 0]) rotate([-15, 0, 90]) cube([10, 11, 2], center = true); // left
color([1, 1, 0]) translate([0, -85, 0]) rotate([0, 15, 90]) cube([10, 11, 2], center = true); // back
color([1, 1, 0]) translate([60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front left
color([1, 1, 0]) translate([-60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front right
color([1, 1, 0]) translate([32.5, 41, 1]) rotate([0, -15, 130]) cube([10, 11, 2], center = true); // front left inner
color([1, 1, 0]) translate([-32.5, 41, 1]) rotate([0, -15, 60]) cube([10, 11, 2], center = true); // front right inner
```
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/XUQ9ZeYk1H">pic.twitter.com/XUQ9ZeYk1H</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508569796253827077?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
And then once I spun it around for a bit and thought it was good, I sliced it in PrusaSlicer and sent it off to Kyubey. It was going to take 14 hours, so I went off to do other things, ate dinner and then went to bed while the printer continued.
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="fr" dir="ltr">Diligent bean <a href="https://t.co/yPgnJA0ZdW">pic.twitter.com/yPgnJA0ZdW</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508397506031460352?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Then when I woke up, Kyubey was done:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/2E1IS810EH">pic.twitter.com/2E1IS810EH</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508407046995156992?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
I was excited and chiseled the print off the bed (the wedges helped a little, but it ended up making the print look kinda weird so I don't know if I will do that again), but the hole for the middle tracker didn't fit perfectly. Everything else did though.
[If you want to get prints off your printer easier, see this video for the method we're starting to use: <br /><br /><iframe width="560" height="315" src="https://www.youtube.com/embed/VCCbzCvtRzU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>](conversation://Mara/hacker)
I looked on my desk and found that a random pen that I had sitting around for months was about the right size, so I pushed it into and out of the hole a few times and then the cables fit perfectly. I assume some plastic was in a weird state or something.
Then I set everything up and I had my Index controller stand:
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="en" dir="ltr">Victory! <a href="https://t.co/A3aCtQMQt5">pic.twitter.com/A3aCtQMQt5</a></p>&mdash; Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508426229464064001?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[I really need to get a table or something for this.](conversation://Cadey/facepalm)
I've uploaded my modified version to [Thingiverse](https://www.thingiverse.com/thing:5332988). If you want to see the OpenSCAD code, you can check it out on GitHub [here](https://github.com/Xe/3dstuff/blob/main/index_stand_hack.scad). I'm really liking OpenSCAD so far. It's very weird but it lets you do whatever you want by chaining together basic shapes to build up to what you want. I imagine I will be using it a lot in the future, especially once my husband's new sim racing gear comes in.
Having a 3D printer around is like having a very weird superpower on standby. You can compile matter in your living room, but you need a very pedantic description of what that should look like. You also can have any material you like as long as it's plastic. However when it's useful, it's a lifesaver. You can make something to fit a gap or mend something broken or even add functionality to something that lacked it. The cloud's the limit!

View File

@ -1,5 +1,5 @@
---
title: "&lt;/kubernetes&gt;"
title: "Goodbye Kubernetes"
date: 2021-01-03
---

View File

@ -136,7 +136,10 @@ Then you can look at `flake.nix` to see what's up:
pkgs = nixpkgsFor.${system};
in
{
go-hello = pkgs.buildGoModule {
# The default package for 'nix build'. This makes sense if the
# flake provides only one package or there is a clear "main"
# package.
default = pkgs.buildGoModule {
pname = "go-hello";
inherit version;
# In 'nix develop', we don't need a copy of the source tree
@ -156,11 +159,6 @@ Then you can look at `flake.nix` to see what's up:
vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
};
});
# The default package for 'nix build'. This makes sense if the
# flake provides only one package or there is a clear "main"
# package.
defaultPackage = forAllSystems (system: self.packages.${system}.go-hello);
};
}
```
@ -197,7 +195,6 @@ Let's take a closer look at the higher level things in the flake:
outputs = { self, nixpkgs }: {
packages = { ... };
defaultPackage = { ... };
};
}
```
@ -418,7 +415,7 @@ world. To use a private repo, your flake input URL should look something like
this:
```
ssh+git://git@github.com:user/repo
git+ssh://git@github.com:user/repo
```
[I'm pretty sure you could use private git repos outside of flakes, however it
@ -462,7 +459,7 @@ nixosModules.bot = { config, lib, ... }: {
Group = "mara-bot";
Restart = "always";
WorkingDirectory = "/var/lib/mara-bot";
ExecStart = "${self.defaultPackage."${system}"}/bin/mara";
ExecStart = "${self.packages."${system}".default}/bin/mara";
};
};
};

View File

@ -19,6 +19,10 @@ chance of bitrotting. I will make every attempt to update it if things change,
however flakes have been fairly consistent for a few years
now.](conversation://Cadey/coffee)
[EDIT(20220327 14:13): A previous version of this article said to use
`defaultPackage` for the default package. This is deprecated and you should use
`packages.default` instead.](conversation://Cadey/coffee)
</div>
[What is a package? I've seen this term thrown around with phrases like "Nix is a
@ -180,10 +184,10 @@ web server template by defining another package:
```nix
# flake.nix
# after defaultPackage
packages = {
default = ...;
docker = let
web = self.defaultPackage.${system};
web = self.packages.${system}.default;
in pkgs.dockerTools.buildLayeredImage {
name = web.pname;
tag = web.version;
@ -373,7 +377,7 @@ the systemd unit:
web-service = pkgs.substituteAll {
name = "web-server.service";
src = ./systemd/web-server.service.in;
web = self.defaultPackage.${system};
web = self.packages.${system}.default;
};
```
@ -399,7 +403,7 @@ Then you can add the bit that builds the portable service:
```nix
portable = let
web = self.defaultPackage.${system};
web = self.packages.${system}.default;
in pkgs.portableService {
inherit (web) version;
name = web.pname;

View File

@ -0,0 +1,443 @@
---
title: "Nix Flakes: Exposing and using NixOS Modules"
date: 2022-04-07
series: nix-flakes
tags:
- nixos
vod:
twitch: https://www.twitch.tv/videos/1437346416
youtube: https://youtu.be/wCZ9SwmgSck
---
Nix flakes allow you to expose NixOS modules. NixOS modules are templates for
system configuration and they are the basis of how you configure NixOS. Today
we're going to take our Nix flake [from the last
article](/blog/nix-flakes-2-2022-02-27) and write a NixOS module for it so that
we can deploy it to a container running locally. In the next post we will deploy
this to a server.
[If you haven't read <a href="/blog/series/nix-flakes">the other articles in
this series</a>, you probably should. This article builds upon the previous
ones.](conversation://Mara/hacker)
NixOS modules are building blocks that let you configure NixOS servers. Modules
expose customizable options that expand out into system configuration.
Individually, each module is fairly standalone and self-contained, but they
build up together into your server configuration like a bunch of legos build
into a house. Each module describes a subset of your desired system
configuration and any options relevant to that configuration.
[You can think about them like Ansible playbooks, but NixOS modules describe the
desired end state instead of the steps you need to get to that end
state. It's the end result of evaluating all of your options against all of the
modules that you use in your configuration.](conversation://Mara/hacker)
NixOS modules are functions that take in the current state of the system and
then return things to add to the state of the system. Here is a basic NixOS
module that enables [nginx](https://nginx.org/):
```nix
{ config, pkgs, lib, ... }:
{
config = {
services.nginx.enable = true;
};
}
```
This function takes in the state of the world and returns additions to the state
of the world. This will use the nginx module that ships with NixOS to give you a
basic nginx setup that has the upstream default configuration in it.
NixOS has a way to run other instances of NixOS with [NixOS
containers](https://nixos.org/manual/nixos/stable/index.html#ch-containers). We
can use them to test our NixOS module as we write it.
[This probably won't work on a non-NixOS machine. You will need to
install NixOS in order to test this. For an easy way to do this, see <a
href="https://github.com/elitak/nixos-infect">nixos-infect</a>, a script you can
put into a cloudconfig when spinning up a new server. You can also <a
href="https://nixos.org/manual/nixos/stable/index.html#sec-installation">install
NixOS manually</a> in a VM, but for now it may be better to use a cloud server
as the path of least resistance. Installing NixOS with a flake will be a part of
a future article in this series.](conversation://Mara/hacker)
In Nix you can merge two attribute sets using the `//` operator. This allows you
to add two attribute sets into one larger one, such as like this:
```
nix-repl> { foo = 1; } // { bar = 2; }
{ bar = 2; foo = 1; }
```
<xeblog-conv name="Mara" mood="hacker">
Important pro tip: the merge operator is NOT recursive. If you try to do
something like:
```
nix-repl> foo = { bar = { baz = "foo"; }; }
nix-repl> (foo // { bar = { spam = "eggs"; }; }).bar
```
You will get:
```
{ spam = "eggs"; }
```
And not:
```
{ baz = "foo"; spam = "eggs"; }
```
This is because the `//` operator prefers things in the right hand side over the
left hand side if both conflict. To recursively merge two attribute sets (using
all elements from both sides), use
[lib.recursiveUpdate](https://nixos.org/manual/nixpkgs/stable/#function-library-lib.attrsets.recursiveUpdate):
```
nix-repl> (pkgs.lib.recursiveUpdate foo bar).bar
{ baz = "foo"; spam = "eggs"; }
```
</xeblog-conv>
We will use this to add the container configuration to the flake at the end of
the flake.nix file. We need to do this because the upper part of the flake with
the `forAllSystems` call will generate a bunch of system-specific attributes for
each system we support. NixOS configurations don't support this level of
granularity.
At the end of your flake.nix (just before the final closing `}`), there should
be a line that looks like this:
```nix
});
```
This is what terminates the `outputs` declaration from all the way at the top.
In order to add the container configuration, you should change this to look like
this:
```nix
}) // {
};
```
Then we can add the container configuration to the flake:
```nix
}) // {
nixosConfigurations.container = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({pkgs, ...}: {
# Only allow this to boot as a container
boot.isContainer = true;
networking.hostName = "gohello";
# Allow nginx through the firewall
networking.firewall.allowedTCPPorts = [ 80 ];
services.nginx.enable = true;
})
];
};
};
```
This will create a container (with the hostname "gohello") that starts nginx and
allows traffic to go to nginx on TCP port 80. You can start up the container
with the `nixos-container` command:
```console
$ sudo nixos-container create gohello --flake .#container
host IP is 10.233.1.1, container IP is 10.233.1.2
```
Then you can start the container with this command:
```console
$ sudo nixos-container start gohello
```
And then we can try to connect to nginx to see if it's working:
```console
$ curl http://10.233.1.2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {}
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```
We have nginx!
Now that we have our container to test with, let's write the configuration for
the service. At a basic level we need the following things:
- A systemd unit for orchestrating the HTTP server process
- nginx configuration to reverse proxy to that HTTP server
Above the container definition, add this basic NixOS module template:
```nix
nixosModule = { config, lib, pkgs, ... }:
with lib;
let cfg = config.xeserv.services.gohello;
in {
options.xeserv.services.gohello = {
enable = mkEnableOption "Enables the gohello HTTP service";
};
config = mkIf cfg.enable {
};
};
```
This will create a NixOS module that will only be enabled when the configuration
setting `xeserv.services.gohello.enable` is set to `true`. Everything else we do
here will build on this.
[You can and probably do want to change the namespace `xeserv` here, it is a
placeholder that is not likely to conflict with anything
else.](conversation://Mara/happy)
Create a basic systemd service with this template:
```nix
config = mkIf cfg.enable {
systemd.services."xeserv.gohello" = {
wantedBy = [ "multi-user.target" ];
serviceConfig = let pkg = self.packages.${system}.default;
in {
Restart = "on-failure";
ExecStart = "${pkg}/bin/web-server";
DynamicUser = "yes";
RuntimeDirectory = "xeserv.gohello";
RuntimeDirectoryMode = "0755";
StateDirectory = "xeserv.gohello";
StateDirectoryMode = "0700";
CacheDirectory = "xeserv.gohello";
CacheDirectoryMode = "0750";
};
};
};
```
<xeblog-conv name="Mara" mood="hacker">
NOTE: If you have been following along since before this article was published,
you will want to be sure to do the following things to your copy of gohello:
* Move the definition of `defaultPackage` into the `packages` attribute set with
the name `default`
* Update `defaultApp` and the other entries to point to
`self.packages.${system}.default` instead of `self.defaultPackage.${system}`
We have updated previous articles and the template accordingly. Annoyingly it
seems that this change is new enough that it isn't totally documented on the
NixOS wiki. We are working on fixing this.
</xeblog-conv>
This will do the following things:
- Start the service on boot (`multi-user.target` fires once the system is "fully
booted" and the network is active)
- Automatically restarts the service when it crashes
- Starts our `web-server` binary when running the service
- Creates a random, unique user account for the service (see
[here](http://0pointer.net/blog/dynamic-users-with-systemd.html) for more
information on how/why this works)
- Creates temporary, home and cache directories for the service, makes sure that
random user has permission to use them (with the specified directory modes
too)
- Enables the service automatically
Then you need to add the nginx configuration. We want this application to have
its own virtual host, so we will need to add that as a configuration option
under the `enable` option:
```nix
domain = mkOption rec {
type = types.str;
default = "gohello.local.cetacean.club";
example = default;
description = "The domain name for gohello";
};
```
[Pro tip: `anything.local.cetacean.club` points to `127.0.0.1`. You can use this
when testing things.](conversation://Mara/happy)
And then we can add the nginx configuration under the systemd service definition:
```nix
services.nginx.virtualHosts.${cfg.domain} = {
locations."/" = { proxyPass = "http://127.0.0.1:3031"; };
};
```
Your module should look like this:
```nix
nixosModule = { config, lib, pkgs, ... }:
with lib;
let cfg = config.xeserv.services.gohello;
in {
options.xeserv.services.gohello = {
enable = mkEnableOption "Enables the gohello HTTP service";
domain = mkOption rec {
type = types.str;
default = "gohello.local.cetacean.club";
example = default;
description = "The domain name for gohello";
};
};
config = mkIf cfg.enable {
systemd.services."xeserv.gohello" = {
wantedBy = [ "multi-user.target" ];
serviceConfig = let pkg = self.packages.${pkgs.system}.default;
in {
Restart = "on-failure";
ExecStart = "${pkg}/bin/web-server";
DynamicUser = "yes";
RuntimeDirectory = "xeserv.gohello";
RuntimeDirectoryMode = "0755";
StateDirectory = "xeserv.gohello";
StateDirectoryMode = "0700";
CacheDirectory = "xeserv.gohello";
CacheDirectoryMode = "0750";
};
};
services.nginx.virtualHosts.${cfg.domain} = {
locations."/" = { proxyPass = "http://127.0.0.1:3031"; };
};
};
};
```
[The service name is overly defensive. It's intended to avoid conflicting with
any other unit on the system named `gohello.service`. Feel free to remove this
part, it is really just defensive devops by design to avoid name
conflicts.](conversation://Mara/hacker)
Then you can add it to the container by importing our new module in its
configuration and activating the gohello service:
```nix
nixosConfigurations.container = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModule
({ pkgs, ... }: {
# Only allow this to boot as a container
boot.isContainer = true;
# Allow nginx through the firewall
networking.firewall.allowedTCPPorts = [ 80 ];
services.nginx.enable = true;
xeserv.services.gohello.enable = true;
})
];
};
```
Then you can update the container's configuration with this command:
```console
$ sudo nixos-container update gohello --flake .#container
reloading container...
```
And finally make a request to the gohello service running in that container:
```console
$ curl http://10.233.1.2 -H "Host: gohello.local.cetacean.club"
hello world :)
```
<xeblog-conv name="Mara" mood="hacker">
Exercises for the reader:
Try adding a [nixos
option](https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules)
that correlates to the `--bind` flag that `gohello` uses as the TCP
address to serve HTTP from. You will want to have the type be
`types.port`. If you are stuck, see
[here](https://github.com/Xe/nixos-configs/tree/master/common/services) for inspiration.
Also try adding `AmbientCapabilities = "CAP_NET_BIND_SERVICE"` and
`CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"` to your `serviceConfig` and
bind `gohello` to port 80 without nginx involved at all.
</xeblog-conv>
You can delete this container with `sudo nixos-container destroy gohello` when
you are done with it.
These are the basics on how to use NixOS modules. Everything else you can do
with them builds off of these fundamental ideas. Modules are templates that
coordinate packages and configuration into your desired system state. Containers
can let you test out modules without having to add them to your currently
running system. Modules declare options and emit configuration based on those
options.
You can also consume NixOS modules from flakes using the input system, however I
will go into more details about this at a later date. If you want more examples
of NixOS modules, I would suggest checking out my
[nixos-configs](https://github.com/Xe/nixos-configs) repository. I have nearly
everything neatly modularized and configurable. If you see anything in there
that is confusing to you, please [reach out](/contact) and ask. I am happy to
answer your questions and your feedback will help me write future posts in this
series.
I also have my "next generation" flakes-based configuration experiments
[here](https://tulpa.dev/cadey/nixos-configs) if you want to read through those.
I have still been porting over things piecemeal, so it is not a complete replica
of my existing configuration.
Next time I will cover how to install NixOS to a server and deploy system
configurations using [deploy-rs](https://github.com/serokell/deploy-rs). This
will allow you to have your workstation build configuration for your servers and
push out all the changes from there.
---
Many thanks to Open Skies for being my fearless editor that helps make these
things shine.
In part of this post I use my new Xeact-powered HTML component for some of the
conversation fragments, but the sizing was off on my iPhone when I tested it. If
you know what I am doing wrong, please [get in touch](/contact).

View File

@ -0,0 +1,53 @@
---
title: Stop Using Politics As A Cudgel To Discourage Experimentation
date: 2022-04-21
tags:
- rant
- systemd
- communityhealth
---
So let's say you get bored one day and you decide you want to do things that god
and man have decreed impossible. Let's also say that this exact thing involves a
tool that just happens to rustle all of the jimmies (for reasons that are not
entirely clear). Then you get it all to a point where you want to submit it
upstream so you can get help experimenting with this tool.
So you submit it to upstream in the experimental branch, expecting very little
pushback so you can get help tinkering with things. But once you submit it
upstream, [all hell breaks
loose](https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/33329).
Stop using politics as a cudgel to discourage experimentation. Yes it involves
systemd. Just because you think that the tool is overcomplicated doesn't mean
that other people don't find it useful. Trying to shut down experimentation is
how you get people to leave the community or give up participating in open
source altogether.
The reactions in that thread are both disappointing and somewhat to be expected.
I don't know why people have such a negative reaction to systemd. It's just an
init system, not a religion. It wouldn't have become a good choice for so much
of the Linux ecosystem without it having solid technical merits. If it is really
that bad then the mantle of responsibility is on you for coming up with a better
option.
[No, OpenRC is not that option. It can be PART OF an option, but it is not a
competitor by itself.](conversation://Cadey/coffee)
I know I said I'd stop ranting on this blog as much, but really this stuff
grinds my gears and I feel that I should use my platform for good in this
regard. This is inexcusable. I want to reiterate that I have _no_ power in this
regard. I am just some random person on a blog that got frustrated at the
reactions to this contribution. Some pushback is acceptable. Accusing a
contributor of ignorance is inexcusable. Comments like this have no place in
open source contributions:
> SysTemD is the STD of operating systems. There is no "one little poke", you
> can't be a little bit pregnant.
Jake, if you're out there reading this: keep doing this thing. It is a fantastic
creation that I thought was impossible. You may have to soft-fork the
distribution to get this to work reliably, but I really want to see where this
rabbit-hole goes.
Keep hacking.

View File

@ -0,0 +1,118 @@
---
title: "What To Do As A Recruiter When A Gender-diverse Person Asks You To Update Their Name"
date: 2022-04-01
---
[I really wish this was an April Fool's post. I had a few ideas planned, but
maybe you will get to see them next year.<br /><br />As a reminder, I am
speaking for myself and not for my employer.](conversation://Cadey/coffee)
This post is directed at all of the recruiters that are reading this blog. This
is a scenario that many of you may not have dealt with. After having an example
of this with a recruiter recently I figure it's a teaching moment.
I am speaking up about this because I know many others who have gone through the
same kinds of problems and have not felt safe to speak up about them. I am not
speaking for those people in this post, but I want to use my platform as a
blogger to amplify the sentiment of what I have heard over the years.
## To Recruiters
As a recruiter, if you are cold-emailing someone, please do the research to get
their name correct. If you do not and someone asks you to correct it, do it.
When gender-diverse people like me get an email that references an out of date
name, it is seen as a sign that the person sending that email has not done their
research before sending that email out into the void.
When you correct that name in your system also make sure to cancel all outgoing
automated emails to that person. The caching layer of the recruiting system may
have already drafted those emails based on a template. If they go out, this will
be seen as a _massive sign of disrespect_. It will also make the person
receiving that email question if you _actually corrected_ the name in that
system or not. It may make the recipient also question if you are just giving
them lip service to save face instead of making a genuine effort to ensure that
the recruiting system has accurate information in it.
This is not a good way to foster the kind of trust needed for a gender-diverse
person to want to choose your employer as the single point of failure for access
to medication, food and regular medical checkups. For many gender-diverse
people, changing jobs can mean an interruption of access to life-saving
medication.
You may get a slightly angry reply if you send out emails with incorrect
information. This can happen because gender-diverse people are likely to feel
like society really doesn't care about them and that they are not being
respected to have agency over their identity. To some this is a fact and not
a feeling. And with
[all](https://www.theguardian.com/us-news/2022/mar/10/idaho-bill-trans-youth-treatment-ban-passes-house)
[of](https://www.washingtonpost.com/dc-md-va/2022/03/17/texas-trans-child-abuse-investigations/)
[the](https://www.nbcnews.com/nbc-out/out-politics-and-policy/alabama-bill-seeks-ban-hormone-treatments-trans-youth-rcna18512)
[actions](https://www.hrc.org/press-releases/breaking-2021-becomes-record-year-for-anti-transgender-legislation)
governments have been taking to directly attack the freedoms and rights to
self-determination that gender-diverse people like me rely on, you can't blame
them for being fed up with the situation. It is not fun to feel like your very
existence is made out to be some black mark of doom on Western civilization. It
is even less fun to be reminded of that when reading your email inbox. Please
understand that we mean well, society is just broken in general.
The least you can do is ensure that you do _any amount of research_ to ensure
that you are using the correct name. It may be a good idea to add the following
text to your recruiting emails (before you brag about fundraising is probably
best, I tune out about then):
> If I got your name incorrect, please let me know what name/pronouns you would
> like me to update our system to use. I got this name from $SOURCE.
Adding the source of where you got that name from can help make this less
stressful for gender-diverse people. People's names are spattered everywhere
across the internet. Letting people know where you got that information from can
help them know what to fix if a fix is needed.
Some chosen names may seem "weird" due to societal biases that serve to ensure
that the primary way that people use to refer to eachother in particular are not
chosen by the people being referred to. Trust that the person on the other end
is being honest about their identity. The truth requires no belief.
If they ask you to update their pronouns, respect that and ensure you use them
without failure. Using the wrong pronouns can be seen as an even worse
disrespect than using the wrong name. You do not want this to happen if your
goal is to find people to hire.
## To Gender-diverse People
Yeah, this situation sucks. I can't disagree. You really do need to assume good
faith as much as you can. Most of these recruiter systems rely on ["data
enrichment" APIs](https://clearbit.com/) and potentially outdated mass scraping
of LinkedIn and people's blogs.
It can help if you make publicly available posts like
[this](/blog/xe-2021-08-07) that unambiguously say what you want people to call
you by. Keep it updated in case journalists decide to compare your chosen name
to mercenary groups.
Try to be as polite and direct as possible. Here is an example of how I have
asked recruiters to update their information in the past:
> Please update your files with the name "Xe Iaso" (capital I). I am
> slowly moving away from "Christine Dodrill" as the name I use to
> represent myself professionally.
If you are moving away from a "dead name", you may want to use something like
this:
> I have no record of a "Christine Dodrill" at this email address. You may want
> to look elsewhere. If you would like to proceed with me instead, here is
> information about me: https://christine.website.
Throw in your pronouns too to be safe.
[I really need to change this blog's domain, but I have such amazing SEO that I
really don't want to break it.](conversation://Cadey/coffee)
Also consider deleting the email and not replying to them. That's totally valid
too unless you are in desperate need for a new employer.
You do not need to justify speaking up about an employer having the wrong name
for you. The truth requires no belief. Speaking the truth to power is the
essence of valor, which is one of the highest forms of love.

View File

@ -1,7 +1,16 @@
let Person =
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
{ Type =
{ name : Text
, tags : List Text
, gitLink : Optional Text
, twitter : Optional Text
}
, default =
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
{ name = ""
, tags = [] : List Text
, gitLink = None Text
, twitter = None Text
}
}
let Author =

View File

@ -36,6 +36,7 @@ img {
.conversation-chat {
align-self: center;
min-width: 0;
}
.gruvbox-dark pre, pre {

75
flake.lock Normal file
View File

@ -0,0 +1,75 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1649676176,
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1650265945,
"narHash": "sha256-SO8+1db4jTOjnwP++29vVgImLIfETSXyoz0FuLkiikE=",
"owner": "nix-community",
"repo": "naersk",
"rev": "e8f9f8d037774becd82fce2781e1abdb7836d7df",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1650647760,
"narHash": "sha256-Ng8CGYLSTxeI+oEux0x+tSRA6K7ydoyfJNQf56ld+Uo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b80f570a92d04e8ace67ff09c34aa48708a5c88c",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1650161686,
"narHash": "sha256-70ZWAlOQ9nAZ08OU6WY7n4Ij2kOO199dLfNlvO/+pf8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1ffba9f2f683063c2b14c9f4d12c55ad5f4ed887",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}

215
flake.nix Normal file
View File

@ -0,0 +1,215 @@
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
naersk.url = "github:nix-community/naersk";
};
outputs = { self, nixpkgs, flake-utils, naersk }:
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = naersk.lib."${system}";
src = ./.;
in rec {
packages = rec {
bin = naersk-lib.buildPackage {
pname = "xesite-bin";
root = src;
buildInputs = with pkgs; [ pkg-config openssl git ];
};
config = pkgs.stdenv.mkDerivation {
pname = "xesite-config";
inherit (bin) version;
inherit src;
buildInputs = with pkgs; [ dhall ];
phases = "installPhase";
installPhase = ''
cd $src
mkdir -p $out
dhall resolve < $src/config.dhall >> $out/config.dhall
'';
};
static = pkgs.stdenv.mkDerivation {
pname = "xesite-static";
inherit (bin) version;
inherit src;
phases = "installPhase";
installPhase = ''
mkdir -p $out
cp -vrf $src/static $out
cp -vrf $src/css $out
'';
};
posts = pkgs.stdenv.mkDerivation {
pname = "xesite-posts";
inherit (bin) version;
inherit src;
phases = "installPhase";
installPhase = ''
mkdir -p $out
cp -vrf $src/blog $out
cp -vrf $src/gallery $out
cp -vrf $src/talks $out
'';
};
default = pkgs.symlinkJoin {
name = "xesite-${bin.version}";
paths = [ config posts static bin ];
};
};
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
# Rust
rustc
cargo
rust-analyzer
cargo-watch
# system dependencies
openssl
pkg-config
# kubernetes deployment
dhall
dhall-json
# dependency manager
niv
# tools
ispell
];
SITE_PREFIX = "devel.";
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
RUST_LOG = "debug";
RUST_BACKTRACE = "1";
GITHUB_SHA = "devel";
};
nixosModules.bot = { config, lib, ... }:
with lib;
let cfg = config.xeserv.services.xesite;
in {
options.within.services.xesite = {
enable = mkEnableOption "Activates my personal website";
useACME = mkEnableOption "Enables ACME for cert stuff";
port = mkOption {
type = types.port;
default = 32837;
example = 9001;
description =
"The port number xesite should listen on for HTTP traffic";
};
domain = mkOption {
type = types.str;
default = "xesite.akua";
example = "christine.website";
description =
"The domain name that nginx should check against for HTTP hostnames";
};
sockPath = mkOption rec {
type = types.str;
default = "/srv/within/run/xesite.sock";
example = default;
description =
"The unix domain socket that xesite should listen on";
};
};
config = mkIf cfg.enable {
users.users.xesite = {
createHome = true;
description = "github.com/Xe/site";
isSystemUser = true;
group = "within";
home = "/srv/within/xesite";
extraGroups = [ "keys" ];
};
systemd.services.xesite = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "xesite";
Group = "within";
Restart = "on-failure";
WorkingDirectory = "/srv/within/xesite";
RestartSec = "30s";
Type = "notify";
# Security
CapabilityBoundingSet = "";
DeviceAllow = [ ];
NoNewPrivileges = "true";
ProtectControlGroups = "true";
ProtectClock = "true";
PrivateDevices = "true";
PrivateUsers = "true";
ProtectHome = "true";
ProtectHostname = "true";
ProtectKernelLogs = "true";
ProtectKernelModules = "true";
ProtectKernelTunables = "true";
ProtectSystem = "true";
ProtectProc = "invisible";
RemoveIPC = "true";
RestrictSUIDSGID = "true";
RestrictRealtime = "true";
SystemCallArchitectures = "native";
SystemCallFilter = [
"~@reboot"
"~@module"
"~@mount"
"~@swap"
"~@resources"
"~@cpu-emulation"
"~@obsolete"
"~@debug"
"~@privileged"
];
UMask = "007";
};
script = let site = packages.default;
in ''
export SOCKPATH=${cfg.sockPath}
export DOMAIN=${toString cfg.domain}
cd ${site}
exec ${site}/bin/xesite
'';
};
services.nginx.virtualHosts."xesite" = {
serverName = "${cfg.domain}";
locations."/" = {
proxyPass = "http://unix:${toString cfg.sockPath}";
proxyWebsockets = true;
};
forceSSL = cfg.useACME;
useACMEHost = "christine.website";
extraConfig = ''
access_log /var/log/nginx/xesite.access.log;
'';
};
};
};
});
}

View File

@ -14,6 +14,7 @@ serde = { version = "1", features = ["derive"] }
thiserror = "1"
tracing = "0.1"
tracing-futures = "0.2"
url = "2"
[dev-dependencies]
tokio = { version = "1", features = ["full"] }

View File

@ -1,7 +1,10 @@
use std::{fs, io, path::Path};
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{debug, error, instrument};
use url::Url;
pub type Campaigns = Vec<Object<Campaign>>;
pub type Pledges = Vec<Object<Pledge>>;
@ -61,17 +64,33 @@ pub struct User {
pub url: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RefreshGrant {
pub access_token: String,
pub refresh_token: String,
pub expires_in: serde_json::Value,
pub scope: serde_json::Value,
pub token_type: String,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("json error: {0:?}")]
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
#[error("request error: {0:?}")]
#[error("request error: {0}")]
Request(#[from] reqwest::Error),
#[error("{0}")]
IO(#[from] io::Error),
#[error("url parse error: {0}")]
URLParse(#[from] url::ParseError),
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq)]
pub struct Credentials {
pub client_id: String,
pub client_secret: String,
@ -105,12 +124,18 @@ pub struct Links {
}
impl Client {
pub fn new(creds: Credentials) -> Self {
Self {
pub fn new() -> Result<Self> {
let mut creds = Credentials::default();
let p = Path::new(".patreon.json");
let config = fs::read_to_string(p)?;
creds = serde_json::from_str(&config)?;
Ok(Self {
cli: reqwest::Client::new(),
base_url: "https://api.patreon.com".into(),
creds: creds,
}
})
}
#[instrument(skip(self))]
@ -157,4 +182,57 @@ impl Client {
let data: Data<Vec<Object<Pledge>>, Object<User>> = serde_json::from_str(&data)?;
Ok(data.included.unwrap())
}
/*
POST www.patreon.com/api/oauth2/token
?grant_type=refresh_token
&refresh_token=<the users refresh_token>
&client_id=<your client id>
&client_secret=<your client secret>
1. grab new creds
2. serialize new creds to disk
3. reload current creds in ram
4. ???
5. profit!
*/
#[instrument(skip(self))]
pub async fn refresh_token(&mut self) -> Result<()> {
let mut u = Url::parse(&self.base_url)?;
u.set_path("/api/oauth2/token");
u.query_pairs_mut()
.append_pair("grant_type", "refresh_token")
.append_pair("refresh_token", &self.creds.refresh_token)
.append_pair("client_id", &self.creds.client_id)
.append_pair("client_secret", &self.creds.client_secret);
let rg: RefreshGrant = self
.cli
.post(&u.to_string())
.header(
"Authorization",
format!("Bearer {}", self.creds.access_token),
)
.send()
.await?
.error_for_status()?
.json()
.await?;
let mut creds = self.creds.clone();
creds.access_token = rg.access_token;
creds.refresh_token = rg.refresh_token;
let p = Path::new(".patreon.json");
if p.exists() {
fs::remove_file(p)?;
}
let mut fout = fs::File::create(p)?;
serde_json::to_writer(&mut fout, &creds)?;
self.creds = creds;
Ok(())
}
}

View File

@ -1,40 +0,0 @@
let
sources = import ./nix/sources.nix;
pkgs =
import sources.nixpkgs { overlays = [ (import sources.nixpkgs-mozilla) ]; };
dhallpkgs = import sources.easy-dhall-nix { inherit pkgs; };
dhall-yaml = dhallpkgs.dhall-yaml-simple;
dhall = dhallpkgs.dhall-simple;
xepkgs = import sources.xepkgs { inherit pkgs; };
rust = pkgs.callPackage ./nix/rust.nix { };
in with pkgs;
with xepkgs;
mkShell {
buildInputs = [
# Rust
rust
cargo-watch
# system dependencies
openssl
pkg-config
# kubernetes deployment
dhall
dhall-yaml
# dependency manager
niv
# tools
ispell
];
SITE_PREFIX = "devel.";
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
RUST_LOG = "debug";
RUST_BACKTRACE = "1";
RUST_SRC_PATH =
"${pkgs.latest.rustChannels.nightly.rust-src}/lib/rustlib/src/rust/library";
GITHUB_SHA = "devel";
}

View File

@ -1,7 +1,16 @@
let Person =
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
{ Type =
{ name : Text
, tags : List Text
, gitLink : Optional Text
, twitter : Optional Text
}
, default =
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
{ name = ""
, tags = [] : List Text
, gitLink = None Text
, twitter = None Text
}
}
in [ Person::{
@ -20,8 +29,8 @@ in [ Person::{
, "istio"
, "typescript"
]
, gitLink = "https://github.com/euforic"
, twitter = "https://twitter.com/euforic"
, gitLink = Some "https://github.com/euforic"
, twitter = Some "https://twitter.com/euforic"
}
, Person::{
, name = "David Roberts"
@ -41,8 +50,8 @@ in [ Person::{
, "embedded"
, "sql"
]
, gitLink = "https://github.com/ddr0"
, twitter = "https://twitter.com/DDR_4"
, gitLink = Some "https://github.com/ddr0"
, twitter = Some "https://twitter.com/DDR_4"
}
, Person::{
, name = "Faizan Jamil"
@ -65,8 +74,7 @@ in [ Person::{
, "full-stack"
, "linux"
]
, gitLink = "https://github.com/faizjamil"
, twitter = "N/A"
, gitLink = Some "https://github.com/faizjamil"
}
, Person::{
, name = "Joseph Crawley"
@ -80,8 +88,8 @@ in [ Person::{
, "bash"
, "linux"
]
, gitLink = "https://github.com/espe-on"
, twitter = "https://twitter.com/espe_on_"
, gitLink = Some "https://github.com/espe-on"
, twitter = Some "https://twitter.com/espe_on_"
}
, Person::{
, name = "nicoo"
@ -96,7 +104,7 @@ in [ Person::{
, "security"
, "SDR"
]
, gitLink = "https://github.com/nbraud"
, gitLink = Some "https://github.com/nbraud"
}
, Person::{
, name = "Prajjwal Singh"
@ -112,8 +120,8 @@ in [ Person::{
, "google-cloud"
, "typescript"
]
, gitLink = "https://github.com/Prajjwal"
, twitter = "https://twitter.com/prajjwalsin"
, gitLink = Some "https://github.com/Prajjwal"
, twitter = Some "https://twitter.com/prajjwalsin"
}
, Person::{
, name = "Piyushh Bhutoria"
@ -125,8 +133,8 @@ in [ Person::{
, "php"
, "google-cloud"
]
, gitLink = "https://github.com/Piyushhbhutoria"
, twitter = "https://twitter.com/PiyushhB"
, gitLink = Some "https://github.com/Piyushhbhutoria"
, twitter = Some "https://twitter.com/PiyushhB"
}
, Person::{
, name = "Ryan Casalino"
@ -143,8 +151,7 @@ in [ Person::{
, "flask"
, "unix"
]
, gitLink = "https://github.com/rjpcasalino"
, twitter = "N/A"
, gitLink = Some "https://github.com/rjpcasalino"
}
, Person::{
, name = "Jeremy White"
@ -163,8 +170,8 @@ in [ Person::{
, "google-cloud"
, "azure"
]
, gitLink = "https://github.com/dudymas"
, twitter = "https://twitter.com/dudymas"
, gitLink = Some "https://github.com/dudymas"
, twitter = Some "https://twitter.com/dudymas"
}
, Person::{
, name = "Zachary McKee"
@ -181,14 +188,12 @@ in [ Person::{
, "nginx"
, "gunicorn"
]
, gitLink = "https://github.com/ZacharyRMcKee"
, twitter = "N/A"
, gitLink = Some "https://github.com/ZacharyRMcKee"
}
, Person::{
, name = "Muazzam Kazmi"
, tags = [ "Rust", "C++", "x86assembly", "WinAPI", "Node.js", "React.js" ]
, gitLink = "https://github.com/muazzamalikazmi"
, twitter = "N/A"
, gitLink = Some "https://github.com/muazzamalikazmi"
}
, Person::{
, name = "Jeffin Mathew"
@ -202,8 +207,8 @@ in [ Person::{
, "javascript"
, "iot"
]
, gitLink = "https://github.com/mjeffin"
, twitter = "https://twitter.com/mpjeffin"
, gitLink = Some "https://github.com/mjeffin"
, twitter = Some "https://twitter.com/mpjeffin"
}
, Person::{
, name = "Nasir Hussain"
@ -218,24 +223,17 @@ in [ Person::{
, "golang"
, "rpm packaging"
]
, gitLink = "https://github.com/nasirhm"
, twitter = "https://twitter.com/_nasirhm_"
, gitLink = Some "https://github.com/nasirhm"
, twitter = Some "https://twitter.com/_nasirhm_"
}
, Person::{
, name = "Avi Parshan"
, tags =
[ "python"
, "windows"
, "javascript"
, "html"
, "android"
, "java"
, "C#"
]
, gitLink = "https://github.com/avipars"
, twitter = "https://twitter.com/aviinfinity"
[ "python", "windows", "javascript", "html", "android", "java", "C#" ]
, gitLink = Some "https://github.com/avipars"
, twitter = Some "https://twitter.com/aviinfinity"
}
, Person:: {
, Person::{
, name = "Tommy Nguyen"
, tags =
[ "c++"
@ -246,6 +244,13 @@ in [ Person::{
, "web"
, "google-cloud-platform"
]
, gitLink = "https://github.com/remyabel"
, gitLink = Some "https://github.com/remyabel"
}
, Person::{
, name = "Krish Jain"
, tags =
[ "c++", "linux", "c", "python", "ios", "nlp", "machine learning" ]
, gitLink = Some "https://github.com/Krish-sysadmin"
, twitter = Some "https://twitter.com/krishjain02"
}
]

View File

@ -1,7 +1,10 @@
use crate::{post::Post, signalboost::Person};
use color_eyre::eyre::Result;
use serde::Deserialize;
use std::{fs, path::PathBuf};
use std::{
fs,
path::{Path, PathBuf},
};
use tracing::{error, instrument};
pub mod markdown;
@ -9,25 +12,26 @@ pub mod poke;
#[derive(Clone, Deserialize)]
pub struct Config {
#[serde(rename = "clackSet")]
pub(crate) clack_set: Vec<String>,
pub(crate) signalboost: Vec<Person>,
pub(crate) port: u16,
#[serde(rename = "resumeFname")]
pub(crate) resume_fname: PathBuf,
#[serde(rename = "webMentionEndpoint")]
pub(crate) webmention_url: String,
#[serde(rename = "miToken")]
pub(crate) mi_token: String,
}
#[instrument]
async fn patrons() -> Result<Option<patreon::Users>> {
use patreon::*;
let creds: Credentials = envy::prefixed("PATREON_")
.from_env()
.unwrap_or(Credentials::default());
let cli = Client::new(creds);
let p = Path::new(".patreon.json");
if !p.exists() {
info!("{:?} does not exist", p);
return Ok(None);
}
let mut cli = patreon::Client::new()?;
if let Err(why) = cli.refresh_token().await {
error!("error getting refresh token: {}", why);
}
match cli.campaign().await {
Ok(camp) => {

View File

@ -232,7 +232,7 @@ async fn main() -> Result<()> {
let _ = std::fs::remove_file(&sockpath);
let uds = UnixListener::bind(&sockpath)?;
axum::Server::builder(ServerAccept { uds })
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>())
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo>())
.await?;
}
Err(_) => {

View File

@ -36,7 +36,7 @@ impl Into<jsonfeed::Item> for Post {
.date_published(self.date.to_rfc3339())
.author(
jsonfeed::Author::new()
.name("Christine Dodrill")
.name("Xe Iaso")
.url("https://christine.website")
.avatar("https://christine.website/static/img/avatar.png"),
);
@ -83,8 +83,7 @@ impl Post {
async fn read_post(dir: &str, fname: PathBuf, cli: &Option<mi::Client>) -> Result<Post> {
debug!(
"loading {}/{}",
dir,
"loading {}",
fname.clone().into_os_string().into_string().unwrap()
);

View File

@ -6,9 +6,9 @@ pub struct Person {
pub tags: Vec<String>,
#[serde(rename = "gitLink")]
pub git_link: String,
pub git_link: Option<String>,
pub twitter: String,
pub twitter: Option<String>,
}
#[cfg(test)]

View File

@ -21,7 +21,12 @@
<div class="cell -4of12 content">
<big>@person.name</big>
<p>@for tag in person.tags { @tag }</p>
<a href="@person.git_link">GitHub</a> - <a href="@person.twitter">Twitter</a>
@if person.git_link.is_some() {
<a href="@person.git_link.unwrap()">GitHub</a>
}
@if person.twitter.is_some() {
<a href="@person.twitter.unwrap()">Twitter</a>
}
</div>
}
</div>