yo i just signed into lemoncurry on my phone!! hopefully this will reduce posting friction and get me actually using my own site for stuff lmao
also it’s been updated a lot since i last posted? lots of general polish, performance tweaks, type checking and such. delicious
not really many new features honestly, just a lot of cleanup and paying off technical debt
anyway yeah i’m feeling pretty good about it! yay 💖
a tale of two releases
hey friends! i haven’t released a lemoncurry update in about a month, but i have been working on it! since last time, i’ve made versions 1.9.3 and 1.9.4 (whoa!! i know, right?) so here’s what happened in both of them :o
first up, lemoncurry 1.9.3!
- bootstrap 4 came out of beta, so i switched from the beta to the final release!! woo
- i set up continuous integration with gitlab ci! what that means is that the unit tests are automatically run every time i push a new commit, and a cute little checkmark is displayed on the commit when i view it on gitlab? unless the tests failed, in which case i get an unpleasant little cross instead? so at a glance i know if a commit Broke The Tests, which is handy info!
- font awesome put out a few more releases, so now i’m on v5.0.8 instead! neat
- lots of refactoring: i previously defined separate named routes in django
for each kind of post, like
entries:articles_index, but that makes figuring out whether a particular url points to an entry or something else way more of a hassle, since there are lots of different route names to match? and it turns out you need to know whether a given url on your site points at an entry to receive webmentions (!!!!!!!) for that entry, so i changed all that stuff around :o now there’s just a route called
entries:indexthat takes a parameter for the entry kind, which is much easier to process c:
- the permalink pages, like
/notes/3and such, weren’t checking whether the entry with id 3 actually existed. which meant it crashed and gave you a 500 server error. now, it checks upfront and returns the correct error if you ask for a nonexistent entry - 404, not found!
and now, lemoncurry 1.9.4!!
- i started writing code to receive webmentions!!!!!!!! it’s nowhere near finished yet, and i haven’t made it discoverable so other people’s sites won’t know to use it, but it can receive mentions, figure out which entry was being mentioned, and save that information to the database?? next, i need to use the job queue to test whether the webmention is valid (i.e., whether the source page really mentions the target page) and then if it’s valid to actually display it someplace :o really good start so far tho!
- i decided to install the lovely highlight.js, for pretty code colours every time i post a code block :3
- previously, the nya.as short urls were being generated by a third-party plugin called django-shorturls, but that plugin hasn’t been updated since 2016! since it doesn’t really do much anyway, i decided to spend a few hours reimplementing its functionality myself. easy peasy
- the big advantage of dropping django-shorturls is that it wasn’t compatible with django 2 - since i’m not using it any more, i upgraded django to 2.0.3!! yay!! it seems pretty much the same as before but still!! yay!!
so that’s what’s up! here’s a pretty little python code block to celebrate :o
def is_cutie(person): """ Test whether the person provided is an absolute cutie pie. """ if person.is_reading_this: return True return True # yes, even people not reading this are cuties
yo i just published lemoncurry 1.9.2! there’s nothing new that you can actually see lol
there are two new things behind the scenes though??
i started writing automated tests?? i’m generally pretty bad at writing tests for my projects so this is kinda notable, i think?? currently the only things covered by my tests are a few utility functions and a couple of the
/.well-knownviews, but it’s a good start and i’m proud ✨
i’m using the lovely pytest?? the main reason i chose it is that it’s got super-smart assertion introspection: if you write tests using the standard python
unittestmodule, then you have to write ugly things like
self.assertEqual(result, 5)into your tests, whereas if you’re using pytest all you need to write is
assert result == 5and you still get the same information when the assertion fails! yay! 🐱
i was hashing passwords with django’s default algorithm, pdkbf2, which is quite good! but it’s not the best password hasher out there, so i’ve now switched to the newer and stronger algorithm argon2 instead! 💖
whoa, it’s lemoncurry 1.9! yep!
i bumped the minor version instead of the patch version, ‘cause lots of stuff happened!
- i added rudimentary support for producing accelerated mobile pages versions of each entry!
- it doesn’t always produce valid amp html, because i haven’t figured out how to convert the html i get out of my markdown parser into amp html?
- but for simple entries, mostly entries that don’t have images in them, it works perfectly c:
- lots of performance improvements
- i cut down on the number of sql queries that lemoncurry needs to make to achieve various things?
- most importantly, rendering an entry requires access to its author, its cats, its syndications, and so on. fetching all this took a lot of queries, but it turns out it could just make a few simpler queries up-front instead!
- i bumped font awesome again!
- it’s 5.0.6 now: i skipped 5.0.5 because 5.0.6 was actually released on the very same day?
- i guess they made a mistake in 5.0.5 that needed urgent fixing or something like that :o
- micropub is better again! specifically, i added support for
GET /micropub, which lets micropub clients ask the server for information
q=syndicate-to, which tells the client what syndication targets the server supports (currently none)
q=config, which tells the client where the micropub media endpoint is (currently nowhere) and what the syndication targets are (still none)
q=source, which tells the client the normalised microformats2 json for a particular entry! clients use this to retrieve entries in an editable format, as part of updating an existing entry. this part works perfectly so far, but because updating isn’t implemented it’s not too useful yet 😉
- additionally, micropub
- does scope checking now! your token must contain the ‘create’ scope if you want to create a new entry! there aren’t any other operations you can perform yet that require a scope, but you’ll need the ‘update’ scope when i implement updating existing entries
- returns better error responses, when you do things like pass a token that’s missing the scope you need? it turns out the micropub spec actually describes the format the errors should be in, which i wasn’t following, oops :3 now i am!
- i turned off google analytics!
- i don’t care about getting that kind of information
- i didn’t like subjecting visitors to google’s tracking
- this isn’t actually part of the app itself, just the way i’ve set up my deployment, but: i realised i had gunicorn running just one lemoncurry worker, which meant i could only handle exactly one request at a time??
- this causes problems if for example your micropub source query works simply by directly requesting the page you asked for, and then parsing it with mf2py! (yes, that’s what lemoncurry’s implementation does 😉 )
- it also causes problems if you expect any decent amount of traffic to your site, since you can only handle one http request from anyone at a time?
- so i fixed that, gunicorn has a couple of workers now c:
you may be wondering why the version is already 1.9.1? the reason is that 1.9.0, while containing all of the above exciting features, was unfortunately broken! while optimising the system’s performance, i accidentally broke the django authentication :o i didn’t notice because i was focused on micropub, which always uses its own tokens rather than the usual session-based authentication? anyway 1.9.1 fixed that
oh, by the way, it won’t roll over to 2.0 with the next minor release? unless i do something massive next, you can expect 1.10 to be the next minor version!
- i added rudimentary support for producing accelerated mobile pages versions of each entry!
yoooo i forgot to work on lemoncurry! again! oops?? here’s 1.8.4
the reason i forgot to work on the site this time is that i bought a new 3ds xl, which arrived the day after i last updated lemoncurry 😳 the last nintendo handheld i bought was a ds lite, so i’ve missed a lot of gaming in the meantime and i have a lot to catch up on :o
i got a special samus edition 3ds, which i guess was released to commemorate the new metroid game released for the platform? it’s really cool, here’s what it looks like
the best part is that it came with a download code for the aforementioned new metroid game, samus returns, which is fantastic???
- it’s a remake of metroid ii: return of samus for the game boy, which apparently was poorly received since it was a lot more linear than the first metroid??
- samus returns, however, is quite possibly the best side-scrolling metroid game. yes, it’s that good
- to progress through the game’s areas you have to find and slay a certain number of metroids in each, thereby unlocking the path to the next area, which is what made metroid ii so linear
- that hasn’t changed, but now each area is absolutely colossal and takes literal hours to explore? you head off down a side path thinking “i’ll just check here quickly before going back to the main path”, and then, you don’t return to that spot for like an hour?? so it’s like you’re playing eight non-linear exploration-focused metroid games in a row??? i love it 💖
- it’s also using utterly gorgeous 3d graphics, despite being a sidescroller, since the 3ds is so very good at 3d graphics. the environments look amazing, samus looks really really cool especially after finding the suit upgrades, everything looks super great.
- this is the first side-scrolling metroid for a dual-screen console, which means it can finally use those two screens in the best possible way: the map - not a minimap, a full-size map - is constantly visible on the touch screen, which makes exploring a delightful breeze. it’s the perfect arrangement for classic metroid gameplay, and it makes me wonder why the ds never got a classic metroid?? silly nintendo
- samus can aim and fire in any direction with the circle pad, rather than only eight directions like every previous side-scrolling metroid - this makes for some really engaging combat, especially while fighting the metroids (which are like, minibosses, i guess?) since they’re flying all over the place and you gotta hit their weak point for massive damage
- she’s also got a new attack, called the “melee counter” - when an enemy charges at her, tapping the x button with good timing will stun the enemy, dramatically weaken them, and automatically aim her gun at them. it doesn’t do a lot of damage, but it means you can wipe out enemies with just a few blasts once they’re stunned! many, many enemies charge at you, including the metroids, so you get to use the melee counter a lot. it makes combat faster and way more interesting than in previous metroids, it’s brilliant 💕
- i love this game please play it ✨
oh yeah, the lemoncurry update!! like last time, i made two little changes
- font awesome 5.0.4 was out, so i bumped my version again! yay! i also found a changelog, which i can’t link since it’s inside the private fa pro repository :o it looks like they fixed a lot of spacing issues with the previous versions? neato burrito 🌯
two little upgrades in 1.8.2!
- i put the
aria-hidden="true"attribute on any icon elements that aren’t necessarily font awesome - fa automatically inserts that attribute for you, but only for its own icons? what it’s for is accessibility:
aria-hiddenindicates that an element is just for presentation and can safely be ignored by screen readers c:
- font awesome 5.0.2 is out so i upgraded again! there are no changelogs for fa 5.x yet so idk what the differences are but yeah
- i put the
also websub now works properly! i bumped the version to 1.8.1
the problem was that superfeedr, despite claiming to support the field name
hub.urlfor passing multiple urls in one ping? doesn’t actually do that? so now i’m just using
hub.urland making several requests to it at once
needing multiple requests isn’t a big deal, since websub pings are handled by the job queue 🐱
two cool things in lemoncurry 1.8
i decided to bump the minor version because there are two fairly big improvements in lemoncurry 1.8!
first up, lemoncurry does pagination!! 📄
- every entry feed page now automatically paginates, ten entries to a page, which is good because i’ve now written thirteen entries here. which actually is.......... not a lot, i guess? but it’d have gotten real slow eventually if it kept on having to load every single entry all the time 😉
- all the paginated feeds use
rel="prev"appropriately, so it’s still very easy for programs to navigate. this is very good for indieweb feed readers, like woodwind, as well as for sufficiently advanced screen readers?
the other exciting thing is improved micropub support! there are two major improvements that make lemoncurry more compliant with the micropub spec and therefore more compatible with a variety of micropub clients
- my previous implementation only supported formencoded requests, which is the default format that all micropub servers are required to handle. however, micropub also defines a json request format, which happens to be more powerful in various ways, so now i support that too!
- to authorise any micropub request, you have to provide a token - i supported
one way of doing this, which is setting the
Authorizationhttp header, but the spec also allows the token to be passed as a query or form parameter, so now i support that too ✨
but what’s next? well, the infrastructure changes i made while implementing those two improvements will make it way easier to implement the other aspects of micropub i haven’t yet, too:
you’re supposed to verify the oauth scopes attached to the client’s micropub token? that’s stuff like “yes, this particular token has been granted permission to create new entries”. you’ve probably seen the little prompts while oauthing with sites before, when you give your third-party twitter clients permission to post tweets and stuff like that. my previous token auth code made scope verification pretty annoying to do, but it should be as simple as a cute little check like this now?
if 'create' not in token: return deny("you can't create new entries!!")
the micropub spec allows you to update existing entries as well as make new ones! however, it only allows updates to be provided in the json request format, not the formencoded request format. so of course that wasn’t possible when i only supported formencoded requests, but it should be easy enough to do now 🐱 yay!
- the auth logic was pretty much hardcoded into the
POST /micropubview handler before, which is bad because you actually need to use it in a few other places too - i had duplicate auth logic in
GET /auth/token, since its whole job is verifying that the token is valid and will be accepted by
POST /micropub? there’s also additional endpoints, like
GET /micropub,that i haven’t implemented yet but that will need to reuse the same logic too
pretty rad?? 🐎