Compare commits
	
		
			14 commits
		
	
	
		
			401d21aed1
			...
			0e999284b9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0e999284b9 | |||
| d4f286d723 | |||
| 0939fc6847 | |||
| 49a28973ce | |||
| 9e54fb8216 | |||
| ca09a1372d | |||
| b8372626bf | |||
| 6f8987d393 | |||
| 342a4af782 | |||
| a5927fbc9a | |||
| 46ee9ff892 | |||
| 79674d4a07 | |||
| 00d710e00b | |||
| 5dae68f714 | 
					 14 changed files with 282 additions and 51 deletions
				
			
		| 
						 | 
					@ -1,2 +1,3 @@
 | 
				
			||||||
blog: blog/:year/:month/:slug/
 | 
					blog: blog/:year/:month/:slug/
 | 
				
			||||||
photos: photos/:year/:month/:slug/
 | 
					photos: photos/:year/:month/:slug/
 | 
				
			||||||
 | 
					twitter: twitter/:year/:month/:slug/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,20 @@
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
title: Eryn Rachel Wells
 | 
					 | 
				
			||||||
layout: single
 | 
					layout: single
 | 
				
			||||||
 | 
					draft: true
 | 
				
			||||||
 | 
					params:
 | 
				
			||||||
 | 
					  renderHeadingAnchors: false
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{< nobreak >}}Ingeniera de software,{{< /nobreak >}}
 | 
					¡Hola! Me llamo Eryn Wells. Este es mi sitio web. ¡Bienvenidos!
 | 
				
			||||||
alfarera, música, y
 | 
					
 | 
				
			||||||
{{< nobreak >}}nerd en general.{{< /nobreak >}}
 | 
					
 | 
				
			||||||
 | 
					## Personal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Soy mujer queer, 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Profesional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Desde 2011, he trabajado como ingeniera de software en varios companías alrededor del área
 | 
				
			||||||
 | 
					del Bahía de San Francisco. En 2016, me uní a [Apple][]. Hoy, trabajo en
 | 
				
			||||||
 | 
					tecnologías que 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,53 +6,61 @@ params:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Hi, I'm Eryn Wells. This is my website. Welcome!
 | 
					Hi, I'm Eryn Wells. This is my website. Welcome!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Latest
 | 
					## Latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here are some of my most recent posts.
 | 
					Here are some of my most recent posts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{< home/latest >}}
 | 
					{{< home/latest >}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Personal
 | 
					## Personal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
I'm a queer trans woman, {{< tess >}}' partner, and mom of [two cats][cats]. I
 | 
					I'm a queer woman, {{< tess >}}' partner, and mom of [two cats][cats]. I
 | 
				
			||||||
was born in Seattle, {{< abbr Washington >}}WA{{< /abbr >}} and grew up in
 | 
					was born in Seattle, {{< abbr Washington >}}WA{{< /abbr >}} and grew up in
 | 
				
			||||||
Phoenix, {{< abbr Arizona >}}AZ{{< /abbr >}}. I attended [Oberlin College][ob]
 | 
					Phoenix, {{< abbr Arizona >}}AZ{{< /abbr >}}. I attended [Oberlin College][]
 | 
				
			||||||
where I got a degree in Computer Science. My pronouns are [she/her][pronouns]. 
 | 
					where I got a degree in Computer Science. My pronouns are [she/her][pronouns]. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can read more about me on my [about][ab] page, or [get in touch][where-am-i].
 | 
					You can read more about me on my [about][ab] page, or [get in touch][where-am-i].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Professional
 | 
					## Professional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
I've worked as a software engineer since 2011 for a variety of companies around
 | 
					I've worked as a software engineer since 2011 for a variety of companies around
 | 
				
			||||||
the San Francisco Bay Area. I joined [Apple][a] in 2016, where I currently work
 | 
					the San Francisco Bay Area. I joined [Apple][] in 2016, where I currently work
 | 
				
			||||||
on password management and authentication technologies.
 | 
					on password management and authentication technologies.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
My [résumé][r] has all the details.
 | 
					My [résumé][r] has all the details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Hobbies
 | 
					## Hobbies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
When I'm not working, you can reliably find me hacking on this website or [some
 | 
					When I'm not working, you can reliably find me hacking on this website or [some
 | 
				
			||||||
coding other project][gh]. I'm also a musician, and play piano, Irish tin
 | 
					other coding project][src]. I'm also a musician: I play piano, Irish tin
 | 
				
			||||||
whistle, and modular synthesizer. Occasionally I [record][bc] [things][sc]. I
 | 
					whistle, and modular synthesizer. Occasionally I [record things][sc] or offer
 | 
				
			||||||
love outer space and astronomy; I will always get excited to look at the moon
 | 
					them [for sale][sc]. I love outer space and astronomy; I will always get excited
 | 
				
			||||||
with you, or check out anything through a telescope. I enjoy [photograhy][p],
 | 
					to look at the moon with you, or check out anything through a telescope. I enjoy
 | 
				
			||||||
mostly as a travel hobby. And I've been practicing iaido, a traditional Japanese
 | 
					[photograhy][p], mostly as a travel hobby. And I've been practicing
 | 
				
			||||||
sword art, since early 2024. Other things I've been into include: bread baking,
 | 
					[iaidō][mjer], a traditional Japanese sword art, at [Nishi Kaigan Iaidō
 | 
				
			||||||
 | 
					Dōjō][nkid] since early 2024. Other things I've been into include: bread baking,
 | 
				
			||||||
bicycling, calligraphy, ceramics, and knitting. 
 | 
					bicycling, calligraphy, ceramics, and knitting. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[a]: https://apple.com
 | 
					
 | 
				
			||||||
 | 
					[Apple]: https://apple.com
 | 
				
			||||||
[ab]: {{< ref "/about" >}}
 | 
					[ab]: {{< ref "/about" >}}
 | 
				
			||||||
[b]: {{< ref "/blog" >}}
 | 
					[b]: {{< ref "/blog" >}}
 | 
				
			||||||
[bc]: https://erynwells.bandcamp.com/releases
 | 
					[bc]: https://erynwells.bandcamp.com/releases
 | 
				
			||||||
[cats]: {{< ref "/cats" >}}
 | 
					[cats]: {{< ref "/cats" >}}
 | 
				
			||||||
[eml]: mailto:Eryn%20Wells<eryn@erynwells.me>
 | 
					[eml]: mailto:Eryn%20Wells<eryn@erynwells.me>
 | 
				
			||||||
[gh]: https://github.com/erynofwales
 | 
					[src]: https://source.erynwells.me/eryn
 | 
				
			||||||
[ig]: https://www.instagram.com/erynofwales
 | 
					[ig]: https://www.instagram.com/erynofwales
 | 
				
			||||||
[m]: https://mastodon.social/@erynofwales
 | 
					[m]: https://mastodon.social/@erynofwales
 | 
				
			||||||
[n]: {{< ref "/now" >}}
 | 
					[n]: {{< ref "/now" >}}
 | 
				
			||||||
[ob]: https://www.oberlin.edu
 | 
					[Oberlin College]: https://www.oberlin.edu
 | 
				
			||||||
[p]: {{< ref "/photos" >}}
 | 
					[p]: {{< ref "/photos" >}}
 | 
				
			||||||
[pronouns]: http://pronoun.is/she
 | 
					[pronouns]: http://pronoun.is/she
 | 
				
			||||||
[r]: {{< ref "/resume" >}}
 | 
					[r]: {{< ref "/resume" >}}
 | 
				
			||||||
[sc]: https://soundcloud.com/purlsnbeeps
 | 
					[sc]: https://soundcloud.com/purlsnbeeps
 | 
				
			||||||
[where-am-i]: {{< ref "/about/where-am-i" >}}
 | 
					[where-am-i]: {{< ref "/about/where-am-i" >}}
 | 
				
			||||||
 | 
					[mjer]: https://en.wikipedia.org/wiki/Mus%C5%8D_Jikiden_Eishin-ry%C5%AB
 | 
				
			||||||
 | 
					[nkid]: https://iaido.org
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								content/blog/2026/thirty-eight/index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								content/blog/2026/thirty-eight/index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					title: "Thirty Eight"
 | 
				
			||||||
 | 
					date: 2026-01-14
 | 
				
			||||||
 | 
					draft: true
 | 
				
			||||||
 | 
					tags:
 | 
				
			||||||
 | 
					  - Me
 | 
				
			||||||
 | 
					  - Birthdays
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'm turning thirty eight today!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Neil Armstrong was 38 when he walked on the moon. (Buzz Aldrin was 39.) Jack
 | 
				
			||||||
 | 
					Swigert was 38 when he timed the 14 second burn that brought Apollo 13 back into
 | 
				
			||||||
 | 
					position for reentry to Earth.
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								content/photos/2025/harvest-moon/harvest-moon.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								content/photos/2025/harvest-moon/harvest-moon.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 268 KiB  | 
							
								
								
									
										23
									
								
								content/photos/2025/harvest-moon/index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								content/photos/2025/harvest-moon/index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					title: "Harvest Moon"
 | 
				
			||||||
 | 
					date: 2025-10-06T22:00:00-07:00
 | 
				
			||||||
 | 
					tags:
 | 
				
			||||||
 | 
					    - Space
 | 
				
			||||||
 | 
					    - Astrophotography
 | 
				
			||||||
 | 
					    - Moon
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Monday October 6 was the full moon. As the closest full moon to the [autumnal
 | 
				
			||||||
 | 
					equinox][], it is the [Harvest Moon][]. It also happened to be a [supermoon][],
 | 
				
			||||||
 | 
					making it slightly bigger in the night sky.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I shot this photo from my back deck with my Fujifilm X-T5 and a Canon 100mm
 | 
				
			||||||
 | 
					macro prime[^1]. Amazingly, this is a handheld shot too. I couldn't find a
 | 
				
			||||||
 | 
					necessary bit of my tripod to mount the camera, so I made do.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[^1]: Photography nerds will notice these are _not_ compatible components; I use
 | 
				
			||||||
 | 
					    a Fringer EF-FX mount adapter for this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[autumnal equinox]: https://en.wikipedia.org/wiki/September_equinox
 | 
				
			||||||
 | 
					[Harvest Moon]: https://en.wikipedia.org/wiki/Full_moon#Harvest_moon
 | 
				
			||||||
 | 
					[supermoon]: https://science.nasa.gov/moon/supermoons/
 | 
				
			||||||
							
								
								
									
										5
									
								
								content/tags/eaoc/_index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								content/tags/eaoc/_index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					title: Erin Anne O'Connor
 | 
				
			||||||
 | 
					linkTitle: EAOC
 | 
				
			||||||
 | 
					slug: eaoc
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
							
								
								
									
										138
									
								
								content/uses/index.tok.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								content/uses/index.tok.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,138 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					title: "mi kepeken e ni"
 | 
				
			||||||
 | 
					draft: true
 | 
				
			||||||
 | 
					toc: open
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Tech
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Hardware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Apple M2 MacBook Air from 2022](https://www.apple.com/macbook-air-13-and-15-m2/)
 | 
				
			||||||
 | 
					: mi olin e ilo sona ni. tenpo pini la mi olin e ilo sona ante. ilo sona ante en
 | 
				
			||||||
 | 
					ilo sona ni li MacBook Air. tenpo ale la mi jo e ilo sona ante. kin la tenpo ale
 | 
				
			||||||
 | 
					la mi jo e ilo sona ni.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Apple Studio Display](https://www.apple.com/studio-display/)
 | 
				
			||||||
 | 
					: I was on the fence about a Cinema Display for a long time. I ordered and
 | 
				
			||||||
 | 
					canceled three times. Ultimately I gave in, and I'm quite happy with it. It's
 | 
				
			||||||
 | 
					mounted on a VESA arm attached to my desktop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Software
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Development
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Neovim](https://neovim.io/)
 | 
				
			||||||
 | 
					: tenpo suli la, mi kepeken e `vim`. ona li ilo nanpa wan pi pali lipu mi.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Visual Studio Code](https://code.visualstudio.com/)
 | 
				
			||||||
 | 
					: tenpo sike mute la mi alasa e VSCode. ona li jo e ijo pona mute.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Xcode](https://developer.apple.com/xcode/)
 | 
				
			||||||
 | 
					: mi pali lon [kulupu kili][apple]. Xcode li ilo pi pali lipu tawa. lon kulupu
 | 
				
			||||||
 | 
					kili la mi kepeken e Xcode. lon tomo mi, mi kepeken kin e Xcode. Xcode li pona.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Fork](https://fork.dev)
 | 
				
			||||||
 | 
					: A GUI client for `git`. I think Fork has totally changed how I use `git`. I
 | 
				
			||||||
 | 
					still use the command line for many things, but working through diffs, staging
 | 
				
			||||||
 | 
					changes, and even rebasing is so much easier in a GUI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Terminal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Terminal.app](https://support.apple.com/guide/terminal/welcome/mac)
 | 
				
			||||||
 | 
					: macOS's built in terminal emulator is solid. The only thing I miss from it now
 | 
				
			||||||
 | 
					and then is "true color" support, and that's only when I try to install a new
 | 
				
			||||||
 | 
					color theme in Neovim and everything goes haywire.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Zsh](https://www.zsh.org)
 | 
				
			||||||
 | 
					: I've been using Zsh since before it was cool. My shell config is full of
 | 
				
			||||||
 | 
					bespoke tweaks, including a prompt theme I'm rather proud of.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Hosting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Digital Ocean](https://www.digitalocean.com)
 | 
				
			||||||
 | 
					: DO has hosted my VPS for a number of years. I moved there from Linode because
 | 
				
			||||||
 | 
					I wanted to run FreeBSD on my server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[FreeBSD](https://www.freebsd.org)
 | 
				
			||||||
 | 
					: My server OS of choice. It's secure, simple, and resilient, and I appreciate
 | 
				
			||||||
 | 
					the less-encumbered BSD license.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[FastMail](https://www.fastmail.com/)
 | 
				
			||||||
 | 
					: I've used FastMail for years now as my email host. Their annual fees are quite
 | 
				
			||||||
 | 
					modest and they have a great feature set including a Sieve email filter editor,
 | 
				
			||||||
 | 
					and Masked Email for genearating ad hoc email addresses. I've never had a
 | 
				
			||||||
 | 
					problem with uptime, and the few times I've contacted support, they've been
 | 
				
			||||||
 | 
					helpful.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Hugo](https://gohugo.io)
 | 
				
			||||||
 | 
					: This whole website is built with Hugo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Mobile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Doppler](https://brushedtype.co/doppler/)
 | 
				
			||||||
 | 
					: File-based music player.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Ivory](https://tapbots.com/ivory/)
 | 
				
			||||||
 | 
					: My Mastodon client of choice for macOS and iOS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Overcast](https://overcast.fm/podcasts)
 | 
				
			||||||
 | 
					: My podcast player of choice.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Reeder](https://www.reederapp.com)
 | 
				
			||||||
 | 
					: RSS feed organizer and reader. I'm doing a lot more RSS these days.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Transit](https://transitapp.com)
 | 
				
			||||||
 | 
					: Helps me stay on top of bus schedules in San Francisco.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Music
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Logic Pro](https://www.apple.com/logic-pro/)
 | 
				
			||||||
 | 
					: Apple's DAW is full of features and plugins (far more than I could ever use)
 | 
				
			||||||
 | 
					and it's hard to beat the corporate discount, much as I also love Ableton.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Shure SM7B](https://www.shure.com/en-US/products/microphones/sm7b)
 | 
				
			||||||
 | 
					: The SM7 is an iconic mic, and has been the go-to for voice actors, podcasters,
 | 
				
			||||||
 | 
					and musicians for 50 years. The excellent podcast Twenty Thousand Hertz did a
 | 
				
			||||||
 | 
					great [deep dive episode][20ksm7] on its history and place in the audio
 | 
				
			||||||
 | 
					industry. I have one on my desk at home, and it makes me sound great on work
 | 
				
			||||||
 | 
					video calls, and the occasional audio recording.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Moog Matriarch](https://www.moogmusic.com/synthesizers?type=202)
 | 
				
			||||||
 | 
					: I'm a bit of a Moog fangirl, and this might be my favorite of their synths.
 | 
				
			||||||
 | 
					It's just hard to beat the sound of a Moog.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Kawaii CA-65](https://kawaius.com/product/ca65/)
 | 
				
			||||||
 | 
					: I've been playing piano since I was seven years old. A digital piano is
 | 
				
			||||||
 | 
					perfect for the small apartments I've called home. This one has a fantastic
 | 
				
			||||||
 | 
					realistic action and it sounds great.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Coffee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Baratza Encore](https://www.baratza.com/product/encoretm-zcg485)
 | 
				
			||||||
 | 
					: It's affordable and does a pretty good job.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Kalita Wave](https://kalitausa.com/products/kalita-wave-185-stainless-steel-coffee-dripper)
 | 
				
			||||||
 | 
					: The Kalita is probably the easist single serve pour over dripper I've used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Chemex](https://www.chemexcoffeemaker.com/six-cup-classic-series-coffeemaker.html)
 | 
				
			||||||
 | 
					: If I'm making coffee for more than just me, the Chemex is the biggest pour
 | 
				
			||||||
 | 
					over vessel I have. Makes yummy coffee too.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Gaming
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Nintendo Switch](https://www.nintendo.com/us/switch/)
 | 
				
			||||||
 | 
					: Most of my gaming happens here. I play a lot of Mario Kart online with
 | 
				
			||||||
 | 
					friends, and I'm a big fan of both of the major Legend of Zelda titles that have
 | 
				
			||||||
 | 
					come out for it: Breath of the Wild and Tears of the Kingdom.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[PlayStation 5](https://www.playstation.com/en-us/ps5/)
 | 
				
			||||||
 | 
					: I also have a PS5. I've played a ton of Horizon: Zero Dawn and the new Final
 | 
				
			||||||
 | 
					Fantasy 7 games.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Analogue Pocket](https://www.analogue.co/pocket)
 | 
				
			||||||
 | 
					: When I want to play my stock of Game Boy games, this thing is great.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[apple]: https://www.apple.com
 | 
				
			||||||
 | 
					[vscode-vim]: https://marketplace.visualstudio.com/items?itemName=vscodevim.vim
 | 
				
			||||||
 | 
					[20ksm7]: https://www.20k.org/episodes/sm7
 | 
				
			||||||
							
								
								
									
										14
									
								
								deploy.sh
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								deploy.sh
									
										
									
									
									
								
							| 
						 | 
					@ -1,14 +0,0 @@
 | 
				
			||||||
#!/usr/bin/env zsh
 | 
					 | 
				
			||||||
# Deploy erynwells.me to my web root
 | 
					 | 
				
			||||||
# Eryn Wells <eryn@erynwells.me>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
print -P "%BFiltering .DS_Store files%b"
 | 
					 | 
				
			||||||
find . -name '.DS_Store' -print -delete
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
print -P "%BGenerating site%b"
 | 
					 | 
				
			||||||
hugo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo
 | 
					 | 
				
			||||||
print -P "%BRsyncing site to nutmeg.erynwells.me%b"
 | 
					 | 
				
			||||||
rsync -avz --no-times --no-perms --delete public/ \
 | 
					 | 
				
			||||||
    eryn@nutmeg.erynwells.me:/srv/www/erynwells.me/html
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
<abbr title="{{ .Get 0 }}">{{ .Inner }}</abbr>
 | 
					<abbr title="{{ .Get 0 }}">{{ .Inner }}</abbr>{{- "" -}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,16 +4,33 @@
 | 
				
			||||||
import os.path as osp
 | 
					import os.path as osp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def root_dir() -> str:
 | 
				
			||||||
 | 
					    '''Return the path to the root of the repository.'''
 | 
				
			||||||
 | 
					    return osp.normpath(osp.join(osp.dirname(__file__), '..', '..'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def assets_path() -> str:
 | 
				
			||||||
 | 
					    '''Return the path to the assets directory.'''
 | 
				
			||||||
 | 
					    path = osp.join(root_dir(), 'assets')
 | 
				
			||||||
 | 
					    return path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def content_path() -> str:
 | 
					def content_path() -> str:
 | 
				
			||||||
    '''Return the path to the content directory.'''
 | 
					    '''Return the path to the content directory.'''
 | 
				
			||||||
    path = osp.abspath(osp.join(osp.dirname(__file__), '..', '..', 'content'))
 | 
					    path = osp.join(root_dir(), 'content')
 | 
				
			||||||
    assert osp.isdir(path)
 | 
					 | 
				
			||||||
    return path
 | 
					    return path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def blog_path() -> str:
 | 
					def blog_path() -> str:
 | 
				
			||||||
 | 
					    '''Return the path to the blog content directory.'''
 | 
				
			||||||
    return osp.join(content_path(), 'blog')
 | 
					    return osp.join(content_path(), 'blog')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def photos_path() -> str:
 | 
					def photos_path() -> str:
 | 
				
			||||||
 | 
					    '''Return the path to the photos content directory.'''
 | 
				
			||||||
    return osp.join(content_path(), 'photos')
 | 
					    return osp.join(content_path(), 'photos')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					content_dir = content_path
 | 
				
			||||||
 | 
					blog_dir = blog_path
 | 
				
			||||||
 | 
					photos_dir = photos_path
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ from .scripting import Command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WeeknotesCommand(Command):
 | 
					class WeeknotesCommand(Command):
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def weeknotes_filename(year: str, week: str) -> str:
 | 
					    def weeknotes_filename(year: str | int, week: str | int) -> str:
 | 
				
			||||||
        return f'weeknotes-{year}w{week}.md'
 | 
					        return f'weeknotes-{year}w{week}.md'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ class WeeknotesCommand(Command):
 | 
				
			||||||
            week_number_str = next_sunday.strftime('%V')
 | 
					            week_number_str = next_sunday.strftime('%V')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return blog.post_path(WeeknotesCommand.weeknotes_filename(
 | 
					        return blog.post_path(WeeknotesCommand.weeknotes_filename(
 | 
				
			||||||
            str(year),
 | 
					            year,
 | 
				
			||||||
            week_number_str
 | 
					            week_number_str
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,12 +14,23 @@ from erynwells_me.metadata import slugify
 | 
				
			||||||
from erynwells_me.paths import photos_path
 | 
					from erynwells_me.paths import photos_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def date_argument(value: str) -> datetime.datetime:
 | 
				
			||||||
 | 
					    parsed_date = datetime.datetime.fromisoformat(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not parsed_date.tzinfo:
 | 
				
			||||||
 | 
					        local_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
 | 
				
			||||||
 | 
					        parsed_date = datetime.datetime.combine(parsed_date.date(), parsed_date.time(), local_timezone)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return parsed_date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_args(argv, *a, **kw):
 | 
					def parse_args(argv, *a, **kw):
 | 
				
			||||||
    parser = argparse.ArgumentParser(*a, **kw)
 | 
					    parser = argparse.ArgumentParser(*a, **kw)
 | 
				
			||||||
    parser.add_argument('-e', '--edit', action='store_true')
 | 
					    parser.add_argument('-e', '--edit', action='store_true')
 | 
				
			||||||
    parser.add_argument('-n', '--dry-run', action='store_true')
 | 
					    parser.add_argument('-n', '--dry-run', action='store_true')
 | 
				
			||||||
    parser.add_argument('-t', '--title')
 | 
					    parser.add_argument('-t', '--title')
 | 
				
			||||||
    parser.add_argument('-s', '--slug')
 | 
					    parser.add_argument('-s', '--slug')
 | 
				
			||||||
 | 
					    parser.add_argument('-d', '--date', type=date_argument)
 | 
				
			||||||
    parser.add_argument('--dump-exif', action='store_true')
 | 
					    parser.add_argument('--dump-exif', action='store_true')
 | 
				
			||||||
    parser.add_argument('photos', nargs='+')
 | 
					    parser.add_argument('photos', nargs='+')
 | 
				
			||||||
    args = parser.parse_args(argv)
 | 
					    args = parser.parse_args(argv)
 | 
				
			||||||
| 
						 | 
					@ -29,7 +40,7 @@ def parse_args(argv, *a, **kw):
 | 
				
			||||||
def main(argv):
 | 
					def main(argv):
 | 
				
			||||||
    args = parse_args(argv[1:], prog=argv[0])
 | 
					    args = parse_args(argv[1:], prog=argv[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    earliest_exif_date: Optional[datetime.datetime] = None
 | 
					    post_date: Optional[datetime.datetime] = args.date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for index, photo in enumerate(args.photos):
 | 
					    for index, photo in enumerate(args.photos):
 | 
				
			||||||
        print(f'image\t\t{photo}')
 | 
					        print(f'image\t\t{photo}')
 | 
				
			||||||
| 
						 | 
					@ -40,14 +51,20 @@ def main(argv):
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raw_exif = image._getexif()
 | 
					        raw_exif = image._getexif()
 | 
				
			||||||
        friendly_exif = {TAGS[k]: v for k, v in raw_exif.items() if k in TAGS}
 | 
					        friendly_exif = {}
 | 
				
			||||||
 | 
					        if raw_exif:
 | 
				
			||||||
 | 
					            friendly_exif = {TAGS[k]: v for k, v in raw_exif.items() if k in TAGS}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        photo_date = None
 | 
				
			||||||
            date_string = f'{friendly_exif["DateTime"]} {friendly_exif["OffsetTime"]}'
 | 
					        if not args.date:
 | 
				
			||||||
            exif_date = datetime.datetime.strptime(date_string, '%Y:%m:%d %H:%M:%S %z')
 | 
					            try:
 | 
				
			||||||
        except KeyError:
 | 
					                date_string = f'{friendly_exif["DateTime"]} {friendly_exif["OffsetTime"]}'
 | 
				
			||||||
            exif_date = datetime.datetime.strptime(friendly_exif["DateTime"], '%Y:%m:%d %H:%M:%S')
 | 
					                photo_date = datetime.datetime.strptime(date_string, '%Y:%m:%d %H:%M:%S %z')
 | 
				
			||||||
        print(f'capture-time\t{exif_date.isoformat()}')
 | 
					            except KeyError:
 | 
				
			||||||
 | 
					                photo_date = datetime.datetime.strptime(friendly_exif["DateTime"], '%Y:%m:%d %H:%M:%S')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            photo_date = args.date
 | 
				
			||||||
 | 
					        print(f'capture-time\t{photo_date.isoformat()}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        iso_rating = friendly_exif.get('ISOSpeedRatings')
 | 
					        iso_rating = friendly_exif.get('ISOSpeedRatings')
 | 
				
			||||||
        if iso_rating:
 | 
					        if iso_rating:
 | 
				
			||||||
| 
						 | 
					@ -66,23 +83,24 @@ def main(argv):
 | 
				
			||||||
        if exposure_time:
 | 
					        if exposure_time:
 | 
				
			||||||
            print(f'exposure-time\t{exposure_time}')
 | 
					            print(f'exposure-time\t{exposure_time}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not earliest_exif_date or exif_date < earliest_exif_date:
 | 
					        if not post_date or photo_date < post_date:
 | 
				
			||||||
            earliest_exif_date = exif_date
 | 
					            post_date = photo_date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if index < len(args.photos) - 1:
 | 
					        if index < len(args.photos) - 1:
 | 
				
			||||||
            print()
 | 
					            print()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not earliest_exif_date:
 | 
					    if not post_date:
 | 
				
			||||||
        earliest_exif_date = datetime.datetime.now()
 | 
					        post_date = datetime.datetime.now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    year = earliest_exif_date.year
 | 
					    year = post_date.year
 | 
				
			||||||
    month = earliest_exif_date.month
 | 
					    month = post_date.month
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.slug:
 | 
					    if args.slug:
 | 
				
			||||||
        name = args.slug
 | 
					        name = args.slug
 | 
				
			||||||
    elif args.title:
 | 
					    elif args.title:
 | 
				
			||||||
        name = slugify(args.title)
 | 
					        name = slugify(args.title)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
 | 
					        photo = args.photos[0]
 | 
				
			||||||
        name = osp.splitext(osp.basename(photo))[0]
 | 
					        name = osp.splitext(osp.basename(photo))[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    post_path_year = os.path.join(photos_path(), f'{year:04}', name)
 | 
					    post_path_year = os.path.join(photos_path(), f'{year:04}', name)
 | 
				
			||||||
| 
						 | 
					@ -100,7 +118,7 @@ def main(argv):
 | 
				
			||||||
        post_path = post_path_year
 | 
					        post_path = post_path_year
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        hugo_command = ['hugo', 'new', '--clock', earliest_exif_date.isoformat(), post_path]
 | 
					        hugo_command = ['hugo', 'new', '--clock', post_date.isoformat(), post_path]
 | 
				
			||||||
        if not args.dry_run:
 | 
					        if not args.dry_run:
 | 
				
			||||||
            result = subprocess.run(hugo_command)
 | 
					            result = subprocess.run(hugo_command)
 | 
				
			||||||
            result.check_returncode()
 | 
					            result.check_returncode()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,8 @@ A Python interface to my personal website, Erynwells.me.
 | 
				
			||||||
'''
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import os.path as osp
 | 
				
			||||||
from typing import List
 | 
					from typing import List
 | 
				
			||||||
from erynwells_me.scripting import Command
 | 
					from erynwells_me.scripting import Command
 | 
				
			||||||
from erynwells_me.weeknotes import WeeknotesCommand
 | 
					from erynwells_me.weeknotes import WeeknotesCommand
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,14 @@ def parse_args(commands: List[Command], argv, *a, **kw):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main(argv):
 | 
					def main(argv):
 | 
				
			||||||
    args = parse_args(COMMANDS, argv[1:], prog=argv[0])
 | 
					    program_name = osp.basename(argv[0])
 | 
				
			||||||
 | 
					    args = parse_args(COMMANDS, argv[1:], prog=program_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scripts_dir = osp.dirname(__file__) 
 | 
				
			||||||
 | 
					    repo_root_dir = osp.normpath(osp.join(scripts_dir, '..'))
 | 
				
			||||||
 | 
					    if os.getcwd() != repo_root_dir:
 | 
				
			||||||
 | 
					        os.chdir(repo_root_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return args.handler(args)
 | 
					    return args.handler(args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue