Compare commits
8 Commits
jwt-testin
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
32ca68ed46 | ||
![]() |
bb60a95453 | ||
![]() |
59508b7d59 | ||
![]() |
f0bf0bb171 | ||
![]() |
13244ddab5 | ||
![]() |
2220b539ea | ||
![]() |
056ae9d058 | ||
![]() |
6522f660ab |
@ -1,25 +0,0 @@
|
||||
name: JWT Testing
|
||||
run-name: JWT Testing
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- jwt-testing
|
||||
|
||||
jobs:
|
||||
jwt-testingh:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# https://github.com/marketplace/actions/generate-oidc-jwt
|
||||
- name: Import Secrets
|
||||
id: import-secrets
|
||||
uses: hashicorp/vault-action@v2
|
||||
with:
|
||||
url: http://vault.avril
|
||||
method: kubernetes
|
||||
role: act-runner-helm-charts
|
||||
secrets: |
|
||||
github/token?org_name=${{ gitea.repository_owner }} token | GITHUB_TOKEN ;
|
||||
|
||||
- name: print
|
||||
run: |
|
||||
echo $GITHUB_TOKEN | base64 | base64
|
@ -1,26 +1,44 @@
|
||||
name: Mirror to GitHub
|
||||
run-name: Mirror to GitHub
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# If I were going to make this as a template, I'd instead use the `${{ github.[...] }} context to determin owner
|
||||
# and name directly from the context.
|
||||
# I'm leaving that off because it'd require a whole other step (to pipe `github.repository` through a splitting
|
||||
# tool, because there's only `github.repository_owner`, no `github.repository_name`)
|
||||
#
|
||||
# (And the token should be retrieved from Vault)
|
||||
- uses: https://gitea.scubbo.org/scubbo/commit-report-sync@main
|
||||
# It's still _so_ baffling to me that this doesn't appear to be available in the `github` context.
|
||||
- name: Determine repo name
|
||||
run: |
|
||||
echo "Test output"
|
||||
echo ${{ github.repository }} | cut -d'/' -f2
|
||||
echo "REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f2)" >> "$GITHUB_ENV"
|
||||
echo
|
||||
echo "from braces"
|
||||
echo "${{ env.REPO_NAME }}"
|
||||
echo "$REPO_NAME"
|
||||
|
||||
- name: Import Secrets
|
||||
id: import-secrets
|
||||
uses: hashicorp/vault-action@v2
|
||||
with:
|
||||
url: http://vault.avril
|
||||
method: kubernetes
|
||||
role: act-runner-helm-charts
|
||||
secrets: |
|
||||
github/token?org_name=${{ gitea.repository_owner }} token | GITHUB_TOKEN ;
|
||||
|
||||
- name: Commit Report Sync
|
||||
uses: https://gitea.scubbo.org/scubbo/commit-report-sync@main
|
||||
with:
|
||||
source_repo_domain: gitea.scubbo.org
|
||||
source_repo_owner: ${{ github.repository_owner }}
|
||||
source_repo_name: ${{ env.REPO_NAME }}
|
||||
target_repo_domain: github.com
|
||||
target_repo_owner: scubbo
|
||||
target_repo_name: gitea-commit-mirror
|
||||
source_repo_domain: gitea.scubbo.org
|
||||
source_repo_owner: scubbo
|
||||
source_repo_name: blogcontent
|
||||
token_for_target_repo: ${{ secrets.PAT_FOR_GITHUB_SYNC }}
|
||||
token_for_target_repo: ${{ steps.import-secrets.outputs.GITHUB_TOKEN }}
|
||||
|
||||
- name: Revoke GitHub token
|
||||
run: |
|
||||
curl -s -H "Authorization: Bearer ${{ env.GITHUB_TOKEN }}" -X DELETE https://api.github.com/installation/token
|
||||
echo "Token revoked"
|
||||
|
@ -1,9 +1,6 @@
|
||||
name: Gitea Actions Demo
|
||||
run-name: ${{ gitea.actor }} is testing out Gitea Actions! 🚀
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
|
52
blog/content/posts/excluding-weeknotes-from-main-page.md
Normal file
52
blog/content/posts/excluding-weeknotes-from-main-page.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "Excluding Weeknotes From Main Page"
|
||||
date: 2025-04-06T21:18:49-07:00
|
||||
tags:
|
||||
- Meta
|
||||
- Tech-Snippets
|
||||
|
||||
---
|
||||
I just went to write up a new [weeknotes]({{< ref "/tags/weeknotes" >}}) post, and noticed that that would have meant that all three previewed posts on my main page would have been weeknotes. That simply will not do! So into the depths of Hugo layouts I ventured once more.
|
||||
<!--more-->
|
||||
The relevant part of the original layout looks like[^line-numbers] this:
|
||||
|
||||
```
|
||||
...
|
||||
{{ $section := where $.Site.RegularPages "Section" "in" $section_name }}
|
||||
{{ $section_count := len $section }}
|
||||
{{ if ge $section_count 1 }}
|
||||
<div class="pa3 pa4-ns w-100 w-70-ns center">
|
||||
{{/* Use $section_name to get the section title. Use "with" to only show it if it exists */}}
|
||||
{{ with $.Site.GetPage "section" $section_name }}
|
||||
<h1 class="flex-none">
|
||||
{{ $.Param "recent_copy" | default (i18n "recentTitle" .) }}
|
||||
</h1>
|
||||
{{ end }}
|
||||
|
||||
{{ $n_posts := $.Param "recent_posts_number" | default 3 }}
|
||||
|
||||
<section class="w-100 mw8">
|
||||
{{/* Range through the first $n_posts items of the section */}}
|
||||
{{ range (first $n_posts $section) }}
|
||||
<div class="relative w-100 mb4">
|
||||
{{ .Render "summary-with-image" }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
...
|
||||
```
|
||||
|
||||
Although the [where function](https://gohugo.io/functions/collections/where) does have a pretty good selection of operators, there's no `not` or `not intersection` - so, although it's possible to [filter to all members which have a particular slice-term contained in some other slice](https://gohugo.io/functions/collections/where/#intersection-comparison), it's not immediately possible to find all members that _don't_ have a given value in a slice-term. Thankfully, [later in the same docs](https://gohugo.io/functions/collections/where/#inequality-test) there's a link to [`collections/complement`](https://gohugo.io/functions/collections/complement/), which does exactly what I want. The final result[^commit] was:
|
||||
|
||||
```
|
||||
...
|
||||
{{ $section_original := where $.Site.RegularPages "Section" "in" $section_name }}
|
||||
{{ $weeknotes := where $section_original "Params.tags" "intersect" (slice "Weeknotes") }}
|
||||
{{ $section := complement $weeknotes $section_original }}
|
||||
...
|
||||
```
|
||||
|
||||
Since I don't want those weeknotes to be undiscoverable, though, I also added a dedicated section for them on the homepage. Pretty happy with how that turned out!
|
||||
|
||||
[^line-numbers]: Hmm, note to self for a TODO - automatically adding line-numbers into monospace blocks would be nice!
|
||||
[^commit]: See the actual commit [here](https://gitea.scubbo.org/scubbo/blogcontent/commit/2220b539ea9e6d61117bc72fe371b3544515e732)!
|
@ -40,7 +40,7 @@ Part of agreeing on a goal is agreeing on the context in which it exists - the e
|
||||
|
||||
T_subbed_K - clarify definitions, reduce to illustrative examples
|
||||
|
||||
[^favourite-laws]: They are, in no particular order: [Ashby's](https://en.wikipedia.org/wiki/Variety_(cybernetics)#Law_of_requisite_variety), [Sturgeon's](https://en.wikipedia.org/wiki/Sturgeon%27s_law), [Brandolini's](https://en.wikipedia.org/wiki/Brandolini%27s_law), [Hyrum's](https://en.wikipedia.org/wiki/API#Hyrums), [Goodhart's](https://en.wikipedia.org/wiki/Goodhart%27s_law), [Hoftstadter's](https://en.wikipedia.org/wiki/Hofstadter%27s_law), and [Conway's](https://en.wikipedia.org/wiki/Conway%27s_law), along with the differently-named [Chesterton's](https://en.wikipedia.org/wiki/G._K._Chesterton#Chesterton's_fence)
|
||||
[^favourite-laws]: They are, in no particular order: [Ashby's](https://en.wikipedia.org/wiki/Variety_(cybernetics)#Law_of_requisite_variety), [Sturgeon's](https://en.wikipedia.org/wiki/Sturgeon%27s_law), [Brandolini's](https://en.wikipedia.org/wiki/Brandolini%27s_law), [Hyrum's](https://en.wikipedia.org/wiki/API#Hyrums), [Goodhart's](https://en.wikipedia.org/wiki/Goodhart%27s_law), [Hoftstadter's](https://en.wikipedia.org/wiki/Hofstadter%27s_law), and [Conway's](https://en.wikipedia.org/wiki/Conway%27s_law), along with the differently-named [Chesterton's](https://en.wikipedia.org/wiki/G._K._Chesterton#Chesterton's_fence). And, while writing this article, I came across [this excellent page](https://hacker-laws.com/) which collects many of the best.
|
||||
[^own-your-own-information]: which - I'm increasingly realizing as I get older and more ~~cynical~~ realistic - was a mistake. Own the information that you create, in such a way that it is portable when you are no longer convenient to the organization providing you with hosting - and recognize that "_an index of useful information_" is itself useful information!
|
||||
[^eponymous-dish]: for similar reasons, if I'm eating at a new restaurant and they have a dish named after the establishment, that's usually my default first choice - though I might there be being a sucker for a subtle priming technique...
|
||||
[^what-is-a-policy]: In my original formulation of this observation, it applied to technical decision-making and system designs. However, I believe it applies to any decision of the form "_What should we do in order to achieve some goal?_"
|
||||
|
@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "Snowboarding Philosophy, and A Linguistic Tangent"
|
||||
date: 2025-04-09T17:54:09-07:00
|
||||
tags:
|
||||
- Real-Life
|
||||
- Snippets
|
||||
|
||||
---
|
||||
Depending on how you look at it, I just finished up either the worst or the best snowboarding season of my life so far.
|
||||
<!--more-->
|
||||
|
||||
It was The Worst Season because:
|
||||
* Snowfall was the sparsest I'd ever experienced, and the timing of my trips always seemed to coincide with bad weather or sparse snow. Generously, only 2 of the days could be described as having good snow.
|
||||
* There were more than the usual number of mishaps, misadventures, and gear failures - I had to replace my ski pants, gloves, and helmet due to a combination of failure or misplacement.
|
||||
* I had the worst fall I've ever taken on a snowboard - so bad I was worried about a concussion afterwards (I was, thankfully, fine) and sported a black eye for a few days afterwards.
|
||||
|
||||
But it was The Best Season because:
|
||||
* I set a new lifetime record for most vertical distance boarded in a season and for top speed in a season[^ski-tracks].
|
||||
* I discovered the simple joy of listening to a playlist with in-helmet speakers.
|
||||
* I experienced the most improvement of technique I've ever noticed - both conscious execution of deliberate technique, and unconcious moments of my body intuitively doing "_the right thing_" before my mind could even recognize there was anything to respond to. Both are great feelings; and the latter is an important reminder of the existence and malleability of the subconcious mind.
|
||||
|
||||
There's a [Discordian](https://www.learnreligions.com/discordianism-95677) example there on how any and all interpretations are true and no truth is inherent, but I wanted instead to record an observation I made[^truthfulness] during this season about The Philosophy Of Snowboarding[^motorcycle]:
|
||||
|
||||
> Something I really like about snowboarding is that it physically forces you to embrace (or at least acknowledge) a philosophy of not letting fear dictate your actions, and of using gentle fluid redirection rather than brute force to achieve your aims.
|
||||
>
|
||||
> When going fast, the two most dangerous things you can do are:
|
||||
> * to give in to the instinct to lean back away from the speed - the board will slip out from under you and you'll lose control and fall
|
||||
> * to try to force yourself to slow down by aggressively planting an edge across the direction of your travel - any kind of bump will make you judder, lose control, catch a *front* edge, and faceplant
|
||||
>
|
||||
> Instead, you need to lean *into* the direction of travel, but smoothly redirect it sideways so the speed gets dampened by moving in a different direction, away from the fall line.
|
||||
|
||||
# A linguistic tangent
|
||||
|
||||
Improvements to my snowboarding technique reminded me of a concept for which I've long sought a name. A definition of the concept would be "_a simple piece of advice that can be usefully applied in different ways, across a wide range of experience-levels of a discipline_". Typically, though perhaps not always, I expect these would arise in physical disciplines - examples I've often used for snowboarding, tennis, and martial arts are "_weight on the front foot_", "_keep your eye on the ball_", and "_bend your legs_", respectively - although I expect there are probably examples in Software Engineering and other mental disciplines too.
|
||||
|
||||
Crucially, this isn't something which is always trivially and obviously true - "_go faster than your opponent and you will win the race_" doesn't strike me as something that can be **re**-interpreted or rediscovered as you become a more proficient racer[^antidote]. I'm talking here about a deceptively-simple concept which a beginner might think they have understood and internalized fully, and have progressed beyond the need to remember; only to encounter a challenging sub-skill or technique which requires them to re-examine the simple advice and apply it in a new context.
|
||||
|
||||
I remember this concept every year or so and poll my linguistically-aligned friends for a name, but I don't think I've ever heard a suitable suggestion. At least now I'll have a blog post to point them to next year rather than having to explain it from scratch!
|
||||
|
||||
[^ski-tracks]: shout out [SkiTracks](https://www.skitracks.com/) for being a rock-solid application and never succumbing to microtransactions, monetization, or any other bullshit.
|
||||
[^truthfulness]: that pesky "_commitment to truth_" mindset that I posess compels me to acknowledge that this is not in fact a direct quotation, but has been paraphrased to elaborate.
|
||||
[^motorcycle]: which reminds me, I'm well overdue to re-read [Zen and the Art of Motorcycle Maintenance](https://en.wikipedia.org/wiki/Zen_and_the_Art_of_Motorcycle_Maintenance) - I suspect that "mid-30's me", having worked in Software Engineering for most of my adult life, will have an even deeper appreciation for it than "late-teens me" did.
|
||||
[^antidote]: though it _could_ potentially be an antidote to overly-complex techniques or mindsets which stray from the core aim - instead of a koan which generates insight by having no answer, a, uhh, "naok"(?) could generate insight by being _so_ obviously true as to prompt you into re-examining assumptions which appear to contradict it.
|
32
blog/content/posts/weeknotes-2025-04-06.md
Normal file
32
blog/content/posts/weeknotes-2025-04-06.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
title: "Weeknotes: 2025-04-06"
|
||||
date: 2025-04-06T21:50:46-07:00
|
||||
tags:
|
||||
- CI/CD
|
||||
- EDH-ELO
|
||||
- Gitea
|
||||
- Vault
|
||||
- Vercel
|
||||
- Weeknotes
|
||||
|
||||
---
|
||||
Looks like I'm averaging about one "weeknotes" post every two weeks. That's actually not too bad!
|
||||
<!--more-->
|
||||
Continuing from my [previous post]({{< ref "/posts/weeknotes-the-third" >}}), I did install the [GitHub Vault Plugin](https://github.com/martinbaillie/vault-plugin-secrets-github) on my Vault, but then I got side-tracked to shaving a _different_ yak - Gitea provides no OIDC token for Gitea Actions, so it's not possible to create a different Vault Role for each repo's actions in order to maintain least-privileges. Instead, I've created a single Vault Role that is accessible (to _every_ repo's Actions) via the `kubernetes` [auth method](https://github.com/hashicorp/vault-action?tab=readme-ov-file#kubernetes). Which is, honestly, _fine_ for this setup (where I'm the owner of all the repos on the forge and so I don't have to worry about permission issues from untrusted actors) - but it's not _right_, dammit!
|
||||
|
||||
Thankfully, the bulk of the work of adding OIDC tokens to Gitea Actions had already been completed [nearly two years ago](https://github.com/go-gitea/gitea/pull/25664), but the original author had lost motivation and the PR was abandoned. Both GoLang (the language in which Gitea is written) and OIDC/JWT are things that I am _moderately_ familiar with, albeit no expert - but, that's enough to have [forked the PR and tried to keep moving it forward](https://github.com/go-gitea/gitea/pull/33945)! I'd be really psyched to get this change merged - even though I didn't author the original change, it would still feel great to help contribute this sizeable feature to an Open Source project that I use and respect. Getting PRs merged is [Glue Work](https://www.noidea.dog/glue), and that's still valuable!
|
||||
|
||||
Other than that:
|
||||
* I've been enjoying playing around with [Vercel](https://vercel.com/home)/[Next.js](https://nextjs.org/) after a highly-respected ex-colleague recommended them (hi Dustin!)
|
||||
* I've put a bit more effort into "EDH ELO", the [webtool I've been tinkering with](https://gitea.scubbo.org/scubbo/edh-elo) to rank my Magic: The Gathering Commander playgroup's decks from match results. Kinda tempted to combine the two and "_Rewrite It In ~~Rust~~React_" :P
|
||||
|
||||
<!--
|
||||
Reminders of patterns you often forget:
|
||||
|
||||
Images:
|
||||

|
||||
|
||||
Internal links:
|
||||
[Link-text](\{\{< ref "/posts/name-of-post" >}})
|
||||
(remove the slashes - this is so that the commented-out content will not prevent a built while editing)
|
||||
-->
|
48
blog/content/posts/weeknotes-2025-04-18.md
Normal file
48
blog/content/posts/weeknotes-2025-04-18.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "Weeknotes: 2025-04-18"
|
||||
date: 2025-04-18T22:12:55-07:00
|
||||
tags:
|
||||
- EDH-ELO
|
||||
- Homelab
|
||||
- K8s
|
||||
- Real-Life
|
||||
- Vault
|
||||
- Vercel
|
||||
- Weeknotes
|
||||
|
||||
---
|
||||
Continuing my pattern of writing "week"notes every fortnight. It's not intentional, I swear, it's just working out that way!
|
||||
<!--more-->
|
||||
|
||||
# What I Did
|
||||
|
||||
* The [Gitea PR for OIDC](https://github.com/go-gitea/gitea/pull/33945) is still open, though it's had a milestone label attached, so I'm hopeful that it'll get merged soon.
|
||||
* I finally got a working replacement PSU for my NAS (the original broke back in early February, and I received two replacements that had the incorrect cables in that time), so was able to get my NAS _properly_ installed back in the rack - until now, it'd been awkwardly half-hanging-out, with a differently-sized PSU _outside_ the case with cables snaking Frankensteinily in. Nice to get that tidied away! Although...
|
||||
* ...power-cycling my NAS (and, therefore, my clusters, both hardware and software) highlighted some cold-start problems of Pods mounting persistence. Thanks to [this issue](https://github.com/kubernetes/kubernetes/pull/119735) I found that updating to a newer version of `k3s` did the trick - but that _itself_ came with a host of teething troubles. Still - they got ironed out, and the cluster is now more resilient and fully-featured for it, and [all it cost me](https://xkcd.com/349/) was a couple hours' sleep :P
|
||||
* I also reinstalled a Raspberry Pi board that had been nonfunctional with a broken SD card for months. Back up to 4 nodes in the cluster!
|
||||
* I implemented a feature in [EDH ELO](https://gitea.scubbo.org/scubbo/edh-elo) that I'd been meaning to do for some time - the [ability to seed the database](https://gitea.scubbo.org/scubbo/edh-elo/commit/9b4e6c3b4d852883a372332461253ef9eae6d014) by directly reading the source-of-truth[^persistence] Google Sheet, rather than me down/uploading `.csv`s every time I wanted to update. Cursor/Claude was a major help - as usual, it couldn't get 100% of the way there by itself, but it got me pretty damn close way faster than I would have with documentation alone.
|
||||
* Along the way, I [tried](https://gitea.scubbo.org/scubbo/helm-charts/commit/6aba9bf11b15b28e790cdeced9dbe73a0062a8f6) using [Vault Sidecar Injection](https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-sidecar#configure-kubernetes-authentication)[^vso]. I'd always found it pretty tricky[^dudo] to compare these and the other methods of Vault injection ([BanzaiCloud's webhook](https://bank-vaults.dev/docs/mutating-webhook/), the [External Secrets Operator](https://external-secrets.io/latest/), probably several others I'm unaware of) - without using them, it's hard to get a handle on their ergonomics. And this is why we homelab!
|
||||
* Perhaps a comparison of these methods would be a blog post in the future! TL;DR of what I found - the VSO writes the data _as_ a Kubernetes Secret, which can be referenced as an Env Variable, whereas VSI writes the data into the Pod's filesystem. Ergonomics aside, VSI should be preferred as Kubernetes Secrets [are not actually entirely secure](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||||
|
||||
# What I'll Do
|
||||
|
||||
## Move to Vercel
|
||||
|
||||
Now that it's been announced at work, I guess I can also write here that - I'll be leaving my current job this coming Thursday 24th, and starting at Vercel on the 28th. I'll be working on their internal DevX Platform, so much of the responsibilities will be the same - tooling, automation, process improvement - but I'm really hopeful that the culture of "_a technology company_" (rather than "_a company that uses technology_") will be more-aligned with how I prefer to work.
|
||||
|
||||
I'm especially excited to work at Vercel in particular, as their product focus will help me to strengthen in two areas where I could benefit from improvement:
|
||||
* [`next.js`](https://nextjs.org/) is a Frontend framework; I can sling some HTML/CSS/JS, but I'm definitely more of a Backend-and-Ops guy, so rounding out that skillset will be a good exercise.
|
||||
* [Turborepo](https://turbo.build/) is a build system intended for monorepos. I have long felt considerable cognitive dissonance at the twin facts that:
|
||||
* Most of the [claimed benefits of Monorepos](https://monorepo.tools/) feel, to me, either like simply "_benefits of good tooling_" (i.e. neither monorepos nor polyrepos are "better", here - good tools are just better than bad tools), or as active _drawbacks_ (I'll save that for another, spicier, post :P ).
|
||||
* And yet, lots of smart people seem to genuinely and enthusiastically find them helpful.
|
||||
|
||||
So, I _must_ be missing some advantage of monorepos - but, unfortunately, it's not the kind of system that you can trivially spin-up on a homelab to experience, you really need to work in a "real" one in order to get a feel for it. I'm hoping that a position at Vercel can give me the opportunity to learn what I'm missing!
|
||||
|
||||
## Continue AI Experimentation
|
||||
|
||||
Having recently been converted to "_AI Development Tools are Useful, Actually_", I'm also interested to see how [v0](https://v0.dev/) stacks up against Claude. I've also been tinkering with self-hosting some AI models[^gpu], and it's really highlighted how patchy my understanding is of the layers of the stack. I'd love to dig a little deeper into understanding those system design concepts.
|
||||
|
||||
[^persistence]: The dream would be for this application _itself_ to be the Source Of Truth. But that requires availability and durability guarantees that are _far_ beyond what I'm willing to commit to at this point. My playgroup's match history is more emotionally important to me than the data of any company I work at! (hello prospective employers. For legal reasons, the preceding comment is a joke)
|
||||
[^vso]: As opposed to the [Vault Secrets Operator](https://blog.scubbo.org/posts/vault-secrets-into-k8s/), which I'd previously written about [here]({{< ref "/posts/base-app-infrastructure" >}}) and [elsewhere]({{< ref "/tags/vault" >}})
|
||||
[^dudo]: Not helped by a Principal Engineer colleague who straight-up stated that he likes to withold information from people because, quote, "_I had to work to get this information, I feel like others should too_". But I digress...
|
||||
[^gpu]: My 4Gb GPU can _just about_ run some of the most stripped-down models, but I sense some more hardware investment in my future...
|
20
blog/content/posts/write-with-sudo.md
Normal file
20
blog/content/posts/write-with-sudo.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Write With Sudo"
|
||||
date: 2025-04-18T22:06:20-07:00
|
||||
tags:
|
||||
- Tech-Snippets
|
||||
|
||||
---
|
||||
A snippet that I find myself needing more often than I'd like to admit - when you've opened a file for editing in `vim`, but when you go to save you are reminded that it needs root permissions to do so.
|
||||
<!--more-->
|
||||
```
|
||||
:w !sudo tee %
|
||||
```
|
||||
|
||||
This translates to:
|
||||
* `:w` - "write this file[^buffer] to..."
|
||||
* `!` _ "...the stdin of the following command:"
|
||||
* `tee %` - "the command `tee`, passed as argument \<the filename currently being edited\>"
|
||||
|
||||
[^buffer]: _Technically_ I think "this buffer" might be more accurate?
|
||||
|
@ -34,7 +34,7 @@ This is a ["Uses" page](https://uses.tech/), detailing some of the tools and oth
|
||||
### Hardware
|
||||
|
||||
* Three Raspberries Pi, and a PowerEdge R430 that I got cheap and refurbished thanks to a tip from a friend.
|
||||
* [iX Systems](https://www.ixsystems.com/) [TrueNAS R-Series](https://www.truenas.com/r-series/), 64GB RAM, 1x1.9TB SSD, 7x6TB HDD, 4xEmpty for expansion. Probably overkill, but I'd rather give myself some room to grow than have to deal with data migrationd and repooling regularly!
|
||||
* [iX Systems](https://www.ixsystems.com/) [TrueNAS R-Series](https://www.truenas.com/r-series/), 64GB RAM, 1x1.9TB SSD, 7x6TB HDD, 4xEmpty for expansion. Probably overkill, but I'd rather give myself some room to grow than have to deal with data migrations and repooling regularly!
|
||||
* [Sysracks 12U 35" Rack](https://www.amazon.com/gp/product/B09KK678CN).
|
||||
* [Quotom Mini PC](https://qotom.net/) w/ 8GB RAM, 64GB SSD, running [OPNSense](https://opnsense.org/) as Firewall and Router
|
||||
* [Ubiquiti UniFi AP AC Pro](https://store.ui.com/us/en/products/uap-ac-pro). I set this up about 4 years ago, and I remember it being a real arse to get working with multiple false starts, but since then it's been pretty much flawless. I briefly experimented with an Eero mesh but that dropped out and needed a restart about every couple of weeks.
|
||||
@ -44,8 +44,10 @@ This is a ["Uses" page](https://uses.tech/), detailing some of the tools and oth
|
||||
* [k3s](https://k3s.io/) is a super-simple way to install a Kubernetes cluster on "_something as small as a Raspberry Pi_". I'm sure it's probably missing some of the bells-and-whistles of the more fully-featured installations, but I've never hit any limitations that mattered to me. You can see the setup in the [pi-tools](https://github.com/scubbo/pi-tools/tree/main/scripts-on-pi)[^out-of-date-naming] repo that I use to configure my homeserver. Configuration and installation is [just these two lines](https://github.com/scubbo/pi-tools/blob/main/scripts-on-pi/controller_setup/1.sh#L67-L70), though there are another 70 or so lines of installing convenience resources (which I should really migrate to a full GitOps location, but eh, who has the time?)
|
||||
* [Helm](https://helm.sh/) and [ArgoCD](https://argo-cd.readthedocs.io/en/stable/) are invaluable for defining and deploying Kubernetes applications, respectively.
|
||||
* I have really enjoyed what tinkering I've done with [cdk8s](https://cdk8s.io/) for Infrastructure-As-Code, but haven't used it in earnest yet. I have been able to use some [jsonnet](https://jsonnet.org/) to achieve some [pretty terse application definitions](https://gitea.scubbo.org/scubbo/helm-charts/src/branch/main/app-of-apps/edh-elo.jsonnet), though.
|
||||
* [Gitea](https://about.gitea.com/) as my [Git forge](https://gitea.scubbo.org); hosting repos and Docker images, and executing workflows with [Gitea Actions](https://docs.gitea.com/usage/actions/overview)
|
||||
* [Vault](https://www.hashicorp.com/en/products/vault) for Secrets management
|
||||
|
||||
More detail to follow! TL;DR - Gitea, Vault, Drone CI (would love to drop that), Grafana, OpenProject, Jellyfin, Crossplane, [democratic-csi](https://github.com/democratic-csi/democratic-csi), KeyCloak, HomeAssistant.
|
||||
More detail to follow! TL;DR - Grafana, OpenProject, Jellyfin, Crossplane, [democratic-csi](https://github.com/democratic-csi/democratic-csi), KeyCloak, HomeAssistant.
|
||||
|
||||
[^ai-optin]: Yes, I know it was opt-in. It still indicates decision-making that I don't want to support.
|
||||
[^out-of-date-naming]: the naming is somewhat out-of-date, since I've added a non-Pi PowerEdge to the cluster - but hey, all engineers know there's nothing to permanent as a temporary name!
|
||||
|
75
blog/layouts/index.html
Normal file
75
blog/layouts/index.html
Normal file
@ -0,0 +1,75 @@
|
||||
{{/* Copy-pasted from ananke's base `index.html`, with an addition to filter-out "weeknotes" that I don't want to show up on the homepage */}}
|
||||
{{/* See the post made in the same commit for explanation */}}
|
||||
|
||||
{{ define "main" }}
|
||||
<article class="cf ph3 ph5-l pv3 pv4-l f4 tc-l center measure-wide lh-copy {{ $.Param "text_color" | default "mid-gray" }}">
|
||||
{{ .Content }}
|
||||
</article>
|
||||
{{/* Define a section to pull recent posts from. For Hugo 0.20 this will default to the section with the most number of pages. */}}
|
||||
{{ $mainSections := .Site.Params.mainSections | default (slice "post") }}
|
||||
|
||||
{{/* Check to see if the section is defined for ranging through it */}}
|
||||
{{range ($mainSections)}}
|
||||
{{/* Derive the section name */}}
|
||||
{{ $section_name := . }}
|
||||
{{/* Create a variable with that section to use in multiple places. */}}
|
||||
{{ $section_original := where $.Site.RegularPages "Section" "in" $section_name }}
|
||||
{{ $weeknotes := where $section_original "Params.tags" "intersect" (slice "Weeknotes") }}
|
||||
{{ $section := complement $weeknotes $section_original }}
|
||||
{{ $section_count := len $section }}
|
||||
{{ if ge $section_count 1 }}
|
||||
<div class="pa3 pa4-ns w-100 w-70-ns center">
|
||||
{{/* Use $section_name to get the section title. Use "with" to only show it if it exists */}}
|
||||
{{ with $.Site.GetPage "section" $section_name }}
|
||||
<h1 class="flex-none">
|
||||
{{ $.Param "recent_copy" | default (i18n "recentTitle" .) }}
|
||||
</h1>
|
||||
{{ end }}
|
||||
|
||||
{{ $n_posts := $.Param "recent_posts_number" | default 3 }}
|
||||
|
||||
<section class="w-100 mw8">
|
||||
{{/* Range through the first $n_posts items of the section */}}
|
||||
{{ range (first $n_posts $section) }}
|
||||
<div class="relative w-100 mb4">
|
||||
{{ .Render "summary-with-image" }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{ if ge $section_count (add $n_posts 1) }}
|
||||
<section class="w-100">
|
||||
<h1 class="f3">{{ i18n "more" }}</h1>
|
||||
{{/* Now, range through the next four after the initial $n_posts items. Nest the requirements, "after" then "first" on the outside */}}
|
||||
{{ range (first 4 (after $n_posts $section)) }}
|
||||
<h2 class="f5 fw4 mb4 dib {{ cond (eq $.Site.Language.LanguageDirection "rtl") "ml3" "mr3" }}">
|
||||
<a href="{{ .RelPermalink }}" class="link black dim">
|
||||
{{ .Title }}
|
||||
</a>
|
||||
</h2>
|
||||
{{ end }}
|
||||
|
||||
{{/* Add a link to the latest weeknotes */}}
|
||||
{{ if ge (len $weeknotes) 1 }}
|
||||
<h1 class="f3">Weeknotes</h1>
|
||||
{{ range (first 4 $weeknotes) }}
|
||||
<h2 class="f5 fw4 mb4 dib {{ cond (eq $.Site.Language.LanguageDirection "rtl") "ml3" "mr3" }}">
|
||||
<a href="{{ .RelPermalink }}" class="link black dim">
|
||||
{{ .Title }}
|
||||
</a>
|
||||
</h2>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* As above, Use $section_name to get the section title, and URL. Use "with" to only show it if it exists */}}
|
||||
{{ with $.Site.GetPage "section" $section_name }}
|
||||
<a href="{{ .RelPermalink }}" class="link db f6 pa2 br3 bg-mid-gray white dim w4 tc">{{ i18n "allTitle" . }}</a>
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{end}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user